题解:
对于多串的子串,我们可以建出广义后缀自动机。
由于本题询问的是(子串出现次数>=k)×len的总和,就将所有串扔到自动机中,爆跳pre并标记。每个点得到
一个经过次数cnt。
若cnt>=k,说明这个点压缩的所有子串都可以作为答案串。
然后处理出每个点到根的树链上所有点的答案。
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 100050 #define ll long long int n,k; char s[N],sum[N]; struct node { int pre,len,trs[28]; int cnt; }p[2*N]; int use[2*N],vis[2*N],ens[N],v[2*N]; struct SAM { int tot,las; SAM(){tot=las=1;} void res(){las=1;} void insert(int c) { int np,nq,lp,lq; np = ++tot; p[np].len = p[las].len+1; for(lp=las;lp&&!p[lp].trs[c];lp=p[lp].pre) p[lp].trs[c]=np; if(!lp)p[np].pre = 1; else { lq = p[lp].trs[c]; if(p[lq].len==p[lp].len+1)p[np].pre = lq; else { nq = ++tot; p[nq] = p[lq]; p[nq].len = p[lp].len+1; p[lq].pre = p[np].pre = nq; while(p[lp].trs[c]==lq) { p[lp].trs[c] = nq; lp = p[lp].pre; } } } las = np; } void dfs(int u) { if(u==1||vis[u])return ;vis[u]=1; v[u] = (p[u].cnt>=k)*(p[u].len-p[p[u].pre].len); dfs(p[u].pre); v[u]+=v[p[u].pre]; } void build() { for(int i=1;i<=n;i++) { int u = 1; for(int j=ens[i-1]+1;j<=ens[i];j++) { u = p[u].trs[sum[j]-'a'+1]; int tmp = u; while(tmp!=1&&use[tmp]<i) { p[tmp].cnt++; use[tmp] = i; tmp = p[tmp].pre; } } } for(int i=2;i<=tot;i++)dfs(i); } }sam; int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) { scanf("%s",s+1); sam.res(); int len = strlen(s+1); for(int j=1;j<=len;j++) { sam.insert(s[j]-'a'+1); sum[ens[i-1]+j]=s[j]; } ens[i]=ens[i-1]+len; } sam.build(); memset(vis,0,sizeof(vis)); for(int i=1;i<=n;i++) { int u = 1; ll c = 0; for(int j=ens[i-1]+1;j<=ens[i];j++) { u = p[u].trs[sum[j]-'a'+1]; c+=v[u]; } printf("%lld ",c); } printf(" "); return 0; }