考虑先建出来广义后缀自动机,统计出来每个节点在多少个字符串里出现过。
只有出现次数 \(>=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;
}