很好的一个自动机的题目。
给原串,和若干个询问串。求原串里有多少个不同子串可以通过询问串循环移动得到。
有点类似求两个串的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;
}