CF235C

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zxin__/article/details/78769792

传送门
题意就是你有一个母串,每次查询给出一个串,问母串有多少个子串和查询串循环同构。
首先建出母串的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;
}

猜你喜欢

转载自blog.csdn.net/zxin__/article/details/78769792
今日推荐