zoukankan      html  css  js  c++  java
  • 洛谷P5404 [CTS2019] 重复

    先转化为总方案数减去每个子串都大于等于 (S) 的方案数。

    (T) 为所统计的字符串。对 (S) 构建 (kmp) 自动机,自动机上的每个节点只保留转移到根节点和转移到 ( ext{next}) 链上字符最大的节点两种转移路径,因为如果 (T) 在匹配时不走最大的字符,(T) 在重复后,一定会出现小于 (S) 的子串。

    (p)(T) 重复无限次后对应的节点,那么节点 (p) 一定满足再加入一个 (T) 后,其又回到了节点 (p),即出现了一个环,该环可能重复经过节点。

    那么考虑 (DP),统计自动机上一个节点走 (m) 步后回到其本身的方案数,复杂度为 (O(n^2m)),要考虑优化。

    发现环可以分为经过根节点和不经过根节点的两种。不经过根节点可以直接判断,经过根节点可以枚举起始节点和第一次转移到根节点前走的距离,预处理出 (f_{i,x}),为从根节点走了 (i) 步到节点 (x) 的方案数,通过预处理的信息即可统计答案。

    统计答案时可以快速算出第一次转移到根节点前的方案数,因为自动机一直不经过根节点前走的路径是唯一的,即一直匹配。

    复杂度为 (O(nm))

    #include<bits/stdc++.h>
    #define maxn 2010
    #define p 998244353
    using namespace std;
    typedef long long ll;
    template<typename T> inline void read(T &x)
    {
        x=0;char c=getchar();bool flag=false;
        while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        if(flag)x=-x;
    }
    int m,n,pos;
    ll tot=1,ans;
    int nxt[maxn],mx[maxn],ch[maxn][30];
    ll f[maxn][maxn];
    char s[maxn];
    int main()
    {
        read(m),scanf("%s",s+1),n=strlen(s+1);
        for(int i=2;i<=n;++i)
        {
            while(pos&&s[i]!=s[pos+1]) pos=nxt[pos];
            if(s[i]==s[pos+1]) pos++;
            nxt[i]=pos;
        }
        for(int x=0;x<=n;++x)
        {
            mx[x]=max(mx[nxt[x]],s[x+1]-'a');
            for(int i=0;i<mx[x];++i) ch[x][i]=-1;
            for(int i=mx[x];i<26;++i)
            {
                if(s[x+1]-'a'==i) ch[x][i]=x+1;
                else
                {
                    int y=nxt[x];
                    while(ch[y][i]==-1) y=nxt[y];
                    ch[x][i]=ch[y][i];
                }
            }
        }
        f[0][0]=1;
        for(int i=0;i<m;++i)
        {
            tot=tot*26%p;
            for(int x=0;x<=n;++x)
            {
                if(!f[i][x]) continue;
                for(int c=mx[x];c<26;++c)
                    f[i+1][ch[x][c]]=(f[i+1][ch[x][c]]+f[i][x])%p;
            }
        }
        for(int x=0;x<=n;++x)
        {
            int y=x;
            for(int i=0;i<m;++i)
                ans=(ans+(25-mx[y])*f[m-i-1][x]%p)%p,y=ch[y][mx[y]];
            if(y==x) ans++;
        }
        printf("%lld",(tot-ans+p)%p);
        return 0;
    }
    
  • 相关阅读:
    flask1 + jinja2 day88
    linux9
    linux8 redis集群槽+docker
    dsadfa
    redis
    aaa
    a
    题目
    java对含有中文的字符串进行Unicode编码
    Java转Double类型经纬度为度分秒格式
  • 原文地址:https://www.cnblogs.com/lhm-/p/13779561.html
Copyright © 2011-2022 走看看