考虑先建出来广义后缀自动机,统计出来每个节点在多少个字符串里出现过。

只有出现次数 \(>=k\) 的节点对我们有用,该节点能贡献的字符串数为 \(c[i]=dis[i]-dis[fa[i]]\)

一个节点对其原串的答案的贡献就是它所有子串的贡献,也就是从根节点到该节点上所有点 \(c\) 值之和。

\(dfs\) 过程中维护一下就好了。

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n, m, k, tot, cnt = 1, las;
const int N = 200010;
int head[N], to[N], nt[N], fa[N], shu[N], now[N], js[N], tr[N][26];
long long len[N], c[N], ans[N];
char s[N];
void add(int f, int t)
{
	to[++tot] = t; nt[tot] = head[f]; head[f] = tot;
}
void Insert(int t, int c)
{
	int p = las, np = ++cnt, q, nq;
	las = np; len[np] = len[p] + 1; shu[np] = t;
	for (; !tr[p][c]; p = fa[p])tr[p][c] = np;
	if (!p)fa[np] = 1;
	else
	{
		q = tr[p][c];
		if (len[q] == len[p] + 1)fa[np] = q;
		else
		{
			nq = ++cnt;
			memcpy(tr[nq], tr[q], sizeof(tr[q]));
			len[nq] = len[p] + 1, fa[nq] = fa[q], fa[np] = fa[q] = nq, js[nq] = js[q], now[nq] = now[q];
			for (; p && tr[p][c] == q; p = fa[p])tr[p][c] = nq;
		}
	}
	for (p = np; p && now[p] != t; p = fa[p])++js[p], now[p] = t;
}
void dfs(int x)
{
	c[x] += c[fa[x]]; ans[shu[x]] += c[x];
	for (int i = head[x]; i; i = nt[i])dfs(to[i]);
}
void solve2()
{
	for (int i = 1; i <= n; ++i)
	{
		scanf("%s", s + 1); m = strlen(s + 1); las = 1;
		for (int j = 1; j <= m; ++j)Insert(i, s[j] - 'a');
	}
	for (int i = 2; i <= cnt; ++i)
	{
		add(fa[i], i);
		if (js[i] >= k)c[i] = len[i] - len[fa[i]];
	}
	dfs(1);
	for (int i = 1; i <= n; ++i)printf("%lld ", ans[i]);
}
int main()
{
	cin >> n >> k;
	solve2();
	return 0;
}