CF235C

传送门
题意就是你有一个母串,每次查询给出一个串,问母串有多少个子串和查询串循环同构。
首先建出母串的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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值