很好的一个自动机的题目。
给原串,和若干个询问串。求原串里有多少个不同子串可以通过询问串循环移动得到。
有点类似求两个串的lcs,但是灵活一点。
首先我们把询问串长度扩大一倍,去掉最后一个字符。因为最后那个字符结尾的情况已经有了。
然后把这个新串拿到SAM中跑一遍,跑的过程就像求lcs差不多,每次判断长度len是否大于询问串长度,以及节点有没有重复加入,来更新答案就好了。
前面自动机的构建以及拓扑排序处理就不说了。
召唤代码君:
#include <iostream> #include <cstdio> #include <cstring> #define maxn 4002000 using namespace std; char s[maxn]; int next[maxn][26],pre[maxn],step[maxn],g[maxn],tag[maxn]; int cnt[maxn],Q[maxn]; int N,last,ans,n,L; int p,q,np,nq; void insert(int x) { np=++N,p=last,step[np]=step[p]+1,last=np,g[np]=1; while (p!=-1 && next[p][x]==0) next[p][x]=np,p=pre[p]; if (p==-1) return; q=next[p][x]; if (step[q]==step[p]+1) { pre[np]=q; return; } nq=++N,step[nq]=step[p]+1,pre[nq]=pre[q]; for (int i=0; i<26; i++) next[nq][i]=next[q][i]; pre[np]=pre[q]=nq; while (p!=-1 && next[p][x]==q) next[p][x]=nq,p=pre[p]; } void process() { for (int i=1; i<=N; i++) cnt[step[i]]++; for (int i=2; i<=N; i++) cnt[i]+=cnt[i-1]; for (int i=1; i<=N; i++) Q[--cnt[step[i]]]=i; for (int i=N-1; i>=0; i--) g[pre[Q[i]]]+=g[Q[i]]; } int main() { pre[0]=-1; scanf("%s",s); for (int i=0; s[i]; i++) insert(s[i]-'a'); process(); scanf("%d",&n); for (int T=1; T<=n; T++) { ans=0; scanf("%s",s+1); L=strlen(s+1); for (int i=1; i<L; i++) s[i+L]=s[i]; s[L+L]=' '; int cur=0,len=0; for (int i=1; s[i]; i++) { int k=s[i]-'a'; while (cur!=-1 && next[cur][k]==0) len=min(len,step[cur]),cur=pre[cur]; if (cur==-1) { cur=0; continue; } len=min(len,step[cur])+1; cur=next[cur][k]; for (; step[pre[cur]]>=L; len=min(len,step[cur])) cur=pre[cur]; if (len>=L && tag[cur]!=T) ans+=g[cur],tag[cur]=T; } printf("%d ",ans); } return 0; }