首先建立广义自动机
对于自动机上的每一个节点,开一个set,记录它能接受的所有子串的编号(不要用multiset,会出现重复的编号),然后在fail树上dfs求出从根节点到当前节点这条路径代表的子串出现在几个字符串中(利用fail树的性质),只有对于每个节点启发式合并它的所有儿子的set就行了
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <set>
#define N 200010
using namespace std;
typedef set<int>::iterator itr;
int n,k,cnt;
string A[N];
int G[N],sum[N];
set<int> sub[N];
struct edge{
int t,nx;
}E[N<<1];
struct SAM_{
int p,cnt;
int next[N][30],stp[N],fail[N];
SAM_(){p=cnt=1;}
inline void add(int x,int id){
x-='a';
int q=++cnt; stp[q]=stp[p]+1; sub[q].insert(id);
while(p&&!next[p][x]) next[p][x]=q,p=fail[p];
if(!p) fail[q]=1;
else{
int np=next[p][x];
if(stp[np]==stp[p]+1) fail[q]=np;
else{
int nq=++cnt; stp[nq]=stp[p]+1;
memcpy(next[nq],next[np],sizeof(next[np]));
fail[nq]=fail[np];
fail[np]=fail[q]=nq;
while(p&&next[p][x]==np) next[p][x]=nq,p=fail[p];
}
}
p=q;
}
}SAM;
inline void Insert(int x,int y){
E[++cnt].t=y; E[cnt].nx=G[x]; G[x]=cnt;
}
void dfs(int x){
for(int i=G[x];i;i=E[i].nx){
int v=E[i].t;
dfs(v);
if(sub[v].size()>sub[x].size()) swap(sub[v],sub[x]);
for(itr i=sub[v].begin();i!=sub[v].end();i++)
sub[x].insert(*i);
sub[v].clear();
}
sum[x]=sub[x].size();
}
int main(){
scanf("%d%d",&n,&k);
if(k>n){
for(int i=1;i<=n;i++) printf("0 ");
return 0;
}
for(int i=1;i<=n;i++){
cin>>A[i]; SAM.p=1;
for(int j=0,len=A[i].size();j<len;j++)
SAM.add(A[i][j],i);
}
for(int i=1;i<=SAM.cnt;i++)
if(SAM.fail[i]) Insert(SAM.fail[i],i);
dfs(1);
for(int i=1;i<=n;i++){
int now=1;
long long Ans=0;
for(int j=0;j<A[i].size();j++){
now=SAM.next[now][A[i][j]-'a'];
while(sum[now]<k&&now) now=SAM.fail[now];
Ans+=SAM.stp[now];
}
printf("%lld ",Ans);
}
}