HihoCoder1465 重复旋律8(后缀自动机)
描述
小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一段音乐旋律可以被表示为一段数构成的数列。
小Hi发现旋律可以循环,每次把一段旋律里面最前面一个音换到最后面就成为了原旋律的“循环相似旋律”,还可以对“循环相似旋律”进行相同的变换能继续得到原串的“循环相似旋律”。
小Hi对此产生了浓厚的兴趣,他有若干段旋律,和一部音乐作品。对于每一段旋律,他想知道有多少在音乐作品中的子串(重复便多次计)和该旋律是“循环相似旋律”。
输入
第一行,一个由小写字母构成的字符串S,表示一部音乐作品。字符串S长度不超过100000。
第二行,一个整数N,表示有N段旋律。接下来N行,每行包含一个由小写字母构成的字符串str,表示一段旋律。所有旋律的长度和不超过 100000。
输出
输出共N行,每行一个整数,表示答案。
- 样例输入
-
abac 3 a ab ca
- 样例输出
-
2 2 1
此题感悟:把trans当成KMP的fail函数,从而后缀自动机可以实现KMP和ac自动机的大部分功能。
注意题中的S=1;
此外字符串处理后再用strlen会出错?好像是。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<string> using namespace std; const int N=1e6+100; int q[N*2],tail,head; int tot,slink[2*N],trans[2*N][26],minlen[2*N],maxlen[2*N],edpts[2*N]; int blue[2*N],ind[2*N],used[2*N]; char str[2*N]; int newstate(int _maxlen,int _minlen,int* _trans,int _slink) { maxlen[++tot]=_maxlen; minlen[tot]=_minlen; slink[tot]=_slink; if(_trans) for(int i=0; i<26; i++) trans[tot][i]=_trans[i]; return tot; } int add_char(char ch,int u) { int c=ch-'a',v=u; int z=newstate(maxlen[u]+1,-1,NULL,0); blue[z]=1;//绿色 while(v&&!trans[v][c]) { trans[v][c]=z; v=slink[v]; } if(!v) { minlen[z]=1; slink[z]=1; ind[0]++; return z; } int x=trans[v][c]; if(maxlen[v]+1==maxlen[x]) { slink[z]=x; minlen[z]=maxlen[x]+1; ind[x]++; return z; } int y=newstate(maxlen[v]+1,-1,trans[x],slink[x]); slink[z]=slink[x]=y; ind[y]+=2; minlen[x]=minlen[z]=maxlen[y]+1; while(v&&trans[v][c]==x) { trans[v][c]=y; v=slink[v]; } minlen[y]=maxlen[slink[y]]+1; return z; } void top_sort() { head=tail=0; for(int i=1;i<=tot;i++)if(!ind[i]) q[++tail]=i; while(head<tail) { int u=q[++head]; if(blue[u]) edpts[u]++; edpts[slink[u]] += edpts[u]; if(!--ind[slink[u]]) q[++tail]=slink[u]; } } void _count() { char c[2*N]; scanf("%s",c); int len,L0,i,u=1,ans=0,L=0;//1 L0=strlen(c); for(i=0;i<L0-1;i++) c[i+L0]=c[i]; len=2*L0-1;//改成strlen就错了!!! for(i=0;i<=tot;i++) used[i]=0; for(i=0;i<len;i++){ while(u!=1&&trans[u][c[i]-'a']==0) { u=slink[u]; L=maxlen[u]; } if(trans[u][c[i]-'a']>0) { u=trans[u][c[i]-'a']; L++; } else { u=1; L=0; }//2 if(L>L0){ while(maxlen[slink[u]]>=L0){ u=slink[u]; L=maxlen[u]; } } if(L>=L0&&!used[u]) { ans+=edpts[u]; used[u]=1; } } printf("%d\n",ans); } int main() { scanf("%s",str); int len=strlen(str),pre=1; tot=1; for(int i=0; i<len; i++) { pre=add_char(str[i],pre); } top_sort(); int T; scanf("%d",&T); while(T--) _count(); return 0; }
It is your time to fight!