传送门
题意就是你有一个母串,每次查询给出一个串,问母串有多少个子串和查询串循环同构。
首先建出母串的SAM。
再将询问串复制一遍,在母串上跑就行了。
但直接这样是有问题的,比方说有一个串”abc”,复制一遍后就是”abcabc”,匹配到第二个a的时候,发现母串没有”abca”这个子串,但却有”bca”这个子串,怎么办?
其实只要加一句话for(;step[pre[u]]+1>=l;z=step[u=pre[u]]);
就行了。
为什么这样就行了?
如果step[pre[u]]+1>=l
,那么已经匹配的长度必定也大于等于询问传参。
因为对于一个字符串,如果在SAM上走到了一个节点,那么该字符串一定还存在一个后缀,能在SAM上走到此节点的pre节点
然后我们可以发现这相当于通过pre边来删除首部已匹配的字符。
这道题其实了我们,不要只想着复制主串,有时想想复制询问串也是可行的。
下面就是蒟蒻的代码,一开始桶排数组开小,WA了一发
#include<cstdio>
#include<cstring>
const int N=2000005;
int ch[N][26],xb,step[N],p,np,q,nq,lst,n,i,x,u,l,a[N],b[N],s[N],z,ans,vi[N],pre[N];
int cnt[26];
char c[N];
int main(){
scanf("%s",c+1);lst=xb=1;
for(i=1;c[i];++i){
step[np=++xb]=step[p=lst]+1;++cnt[x=c[i]-'a'];
for(;!ch[p][x];p=pre[p])ch[p][x]=np;
if(p){
q=ch[p][x];
if(step[p]+1!=step[q]){
step[nq=++xb]=step[p]+1;
memcpy(ch[nq],ch[q],104);
pre[nq]=pre[q];
pre[q]=pre[np]=nq;
for(;ch[p][x]==q;p=pre[p])ch[p][x]=nq;
}else pre[np]=q;
}else pre[np]=1;
s[lst=np]=1;
}
for(i=1;i<=xb;++i)++b[step[i]];
for(i=1;b[i];++i)b[i]+=b[i-1];
for(i=1;i<=xb;++i)a[b[step[i]]--]=i;
for(i=xb;i;--i)s[pre[a[i]]]+=s[a[i]];
scanf("%d",&n);
while(n){
scanf("%s",c+1);l=strlen(c+1);
if(l==1){printf("%d\n",cnt[c[1]-'a']);--n;continue;}
memcpy(c+l+1,c+1,l-1);
u=1;z=ans=0;
for(i=1;i<l<<1;++i){
x=c[i]-'a';
if(!ch[u][x]){
for(;u && !ch[u][x];u=pre[u]);
if(ch[u][x])z=step[u]+1,u=ch[u][x];
else z=0,u=1;
}else u=ch[u][x],++z;
if(z>=l && vi[u]!=n)vi[u]=n,ans+=s[u];
for(;step[pre[u]]+1>=l;z=step[u=pre[u]]);
}
printf("%d\n",ans);--n;
}
return 0;
}