题:http://acm.hdu.edu.cn/showproblem.php?pid=6194
题意:求出现恰好k次的子串数量
分析:也就是f数组==k的maxlen[i]-maxlen[ slink[i] ]的总和;
因为f数组表示endpos的集合大小,也就是状态在多少个位置出现过,那么这个状态的所有子串maxlen[i]-maxlen[slink[i]]个都出现过几次
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> using namespace std; typedef long long ll; const int M=1e5+3; int trans[M<<1][26],slink[M<<1]; ll maxlen[M<<1]; ll f[M<<1]; int last,tot,root; char s[M]; int que[M<<1],in[M<<1]; int k; ll res=0; void init(){ tot=last=root=1; memset(in,0,sizeof(in)); memset(f,0,sizeof(f)); memset(trans,0,sizeof(trans)); memset(slink,0,sizeof(slink)); memset(maxlen,0,sizeof(maxlen)); } void extend(int c){ maxlen[++tot]=maxlen[last]+1; int p=last,np=tot; f[np] = 1;///maxlen的串为主串的前缀时才为1 while(p&&!trans[p][c]){ trans[p][c]=np; p=slink[p]; } ///在之前构造的sam中出现了现在的后缀 if(!p) slink[np]=root; else{ int q=trans[p][c]; if(maxlen[p]+1!=maxlen[q]){///若p+c不是q中最大的字符串, ///新建个克隆节点,把p+c从q中挑出来 maxlen[++tot]=maxlen[p]+1; int nq=tot; memcpy(trans[nq],trans[q],sizeof(trans[q])); slink[nq]=slink[q]; slink[q]=slink[np]=nq; while(p&&trans[p][c]==q){ trans[p][c]=nq; p=slink[p]; } } else///否则np直接link连接q slink[np]=q; } last=np; } void tuopu(){ int l=1,r=0; for(int i=root+1;i<=tot;i++)in[slink[i]]++; for(int i=root+1;i<=tot;i++)if(!in[i]) que[++r]=i; while(l<=r){ int x=que[l++]; f[slink[x]]+=f[x]; if(--in[slink[x]]==0) que[++r]=slink[x]; } for(int i=root+1;i<=tot;i++) if(f[i]==k) res+=maxlen[i]-maxlen[slink[i]]; } int main(){ int T; scanf("%d",&T); while(T--){ res=0; scanf("%d%s",&k,s+1); init(); int n=strlen(s+1); for(int i=1;i<=n;i++) extend(s[i]-'a'); tuopu(); printf("%lld ",res); } return 0; }