• 【题解】Rusty String [CF827E]


    【题解】Rusty String [CF827E]

    传送门:( ext{Rusty String}) ( ext{[CF827E]})

    【题目描述】

    多组数据,每组数据给出一个由 (V,K,?) 三种字符组成的字符串,其中 (?) 可以替换为 (V,K) 中的任意一个,求替换后的字符串可以存在的循环周期有哪些。

    【分析】

    乍一看像是带通配符的字符串匹配(不会的强烈建议去康康这个),由于字符集不大,按照套路就应该枚举两个字符 (a e bin{V,K}),然后设 (f(i)=[A[i]=a],) (g(i)=[A[i]=b])

    则有: (PA(k)=sum_{i=1}^{n}f(i)g(i+k)),翻转 (f) 得到:(PA(k)=sum_{i=1}^{n}f(n-i+1)g(i+k)) (=sum_{i+j=n+k+1}f(i)g(j))

    很明显是个 ( ext{FFT}) 的板子,把式子卷起来就可以了。

    按照以往的“经验”,或者说惯性思维,多半会这样做(比如说我),但写出来却发现酱紫连第一个样例都过不了。

    那么问题出在哪儿了呢?

    注意:当 (PA(k) e 0) 时说明在某个地方出现了 ('V')('K') 相对应的情况,以 (k) 作为周期肯定不合法,但 (PA(k) = 0) 并不能说明 (k) 一定是合法周期,因为这道题中的特殊符号不是通配符,而是未知字符,通配符可以任意匹配,但未知字符只能匹配一种。

    比如样例 (1),用上面的错误算法多输出了一个 (2),在这种情况下 (S_3('?')) 分别与 (S_1('V'),S_5('K')) 进行了匹配,因此以 (2) 作为周期是不合法的。

    考虑改进算法:

    之前的做法错在算出来的式子只进行了一周目的匹配判断,那么我们只要将二周目、三周目……全部都判断一下就好了呀,正巧序列 (PA) 完美的包含了所有长度的循环情况,所以直接暴力枚举周期的倍数即可(复杂度为 (O(frac{n}{1}+frac{n}{2}+frac{n}{3} cdots frac{n}{n})=O(nlogn)))。

    总时间复杂度为:(O(nlogn))

    你说啥?卡常?玄学优化减少调用次数?不存在的,我写了这么久的 ( ext{NTT}),还从未被卡过(小骄傲)。

    ( ext{emm...})

    等等,还没完,仔细看题啊:“保证输入字符串总长不超过 (5∗10^5) 。” 注意是输入总长,所以不要偷懒用 ( ext{memset})

    【Code】

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #define LL long long
    #define Re register int
    using namespace std;
    const int N=1048576+3,P=998244353,G=3;
    int n,T,ans,invG,f[N],g[N],tr[N],PA[N],can[N];char A[N];
    inline void in(Re &x){
        int f=0;x=0;char c=getchar();
        while(c<'0'||c>'9')f|=c=='-',c=getchar();
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=f?-x:x;
    }
    inline int mi(Re x,Re k){
        Re s=1;
        while(k){
            if(k&1)s=(LL)s*x%P;
            x=(LL)x*x%P,k>>=1;
        }
        return s;
    }
    inline int inv(Re x){return mi(x,P-2);}
    inline void NTT(Re *f,Re n,Re op){
        for(Re i=0;i<n;++i)if(i<tr[i])swap(f[i],f[tr[i]]);
        for(Re p=2;p<=n;p<<=1){
            Re len=p>>1,w1=mi(op?invG:G,(P-1)/p);
            for(Re st=0;st<n;st+=p)
                for(Re j=st,base=1;j<=st+len-1;++j){
                    Re tmp=(LL)base*f[j+len]%P;
                    f[j+len]=(f[j]-tmp+P)%P,(f[j]+=tmp)%=P;
                    base=(LL)base*w1%P;
                }
        }
    }
    inline void sakura(Re *f,Re n,Re *g,Re m){//卷卷卷
        Re n_=n;
        for(m+=n,n=1;n<=m;n<<=1);Re invn=inv(n);
        for(Re i=n_+1;i<=n;++i)f[i]=g[i]=0;//注意初始化
        for(Re i=1;i<n;++i)tr[i]=(tr[i>>1]>>1)|((i&1)?n>>1:0);
        NTT(f,n,0),NTT(g,n,0);
        for(Re i=0;i<n;++i)f[i]=(LL)f[i]*g[i]%P;
        NTT(f,n,1);
        for(Re i=0;i<=m;++i)f[i]=(LL)f[i]*invn%P;
    }
    int main(){
    //    freopen("123.txt","r",stdin);
        in(T),invG=inv(G);
        while(T--){
            in(n),scanf("%s",A+1),ans=0;
            for(Re i=1;i<=n;++i)PA[i]=0;//注意初始化
            for(Re i=1;i<=n;++i)f[n-i+1]=(A[i]=='K'),g[i]=(A[i]=='V');
            sakura(f,n,g,n);
            for(Re i=1;i<n;++i)PA[i]+=f[i+n+1];//PA[n]可以不用管,因为一定合法
            for(Re i=1;i<=n;++i)f[n-i+1]=(A[i]=='V'),g[i]=(A[i]=='K');
            sakura(f,n,g,n);
            for(Re i=1;i<n;++i)PA[i]+=f[i+n+1];
            for(Re i=1;i<=n;++i){
                can[i]=1;
                for(Re j=i;j<=n&&can[i];j+=i)can[i]&=(!PA[j]);//枚举倍数依次判断
                ans+=can[i];
            }
            printf("%d
    ",ans);
            for(Re i=1;i<=n;++i)if(can[i])printf("%d ",i);puts("");
        }
    }
    
  • 相关阅读:
    Classification and Representation(分类与表示)
    静态链表
    拓扑序列
    二分图问题
    大数据概述
    QT出现应用程序无法正常启动0xc000007b的错误
    简易有穷自动机实验
    一个简易的C语言文法
    词法分析器实验报告
    浅谈词法分析器
  • 原文地址:https://www.cnblogs.com/Xing-Ling/p/12377227.html
Copyright © 2020-2023  润新知