P5404 [CTS2019]重复 KMP自动机

题意:

戳这里

分析:

  • 暴力

暴力枚举,最小表示法判断,复杂度 ((26^m*m)) 期望得分 10pts

  • 正解

转化要求,求不合法的串的个数

我们只要保证构造的无限长串的任意长度为 |s| 的子串的字典序都大于 s ,那么这个串就是不合法的

这样对于不合法串在 s 的 KMP自动机 上匹配的时候,只有两类转移边,一种是转移到根节点,一种是转移到 nxt​ 链上最大的节点

我们记 (P(x)) 表示串 x 匹配的结束的位置,长度为无限大的串匹配结束的位置为 (P(t^{infty}))

很显然对于 (P(t^{infty}+t)=P(t^{infty})) ,所以对于那些再匹配一个 (t) 后回到自己的点,就是 (P(t^{infty}))

所以我们得到了一个方法,我们只需要找到一个节点,满足走 (|t|) 步之后可以回到自己,那么这条路径就是一种合法的 (t),枚举节点然后DP,复杂度 (O(n^2m))

考虑怎么优化,我们发现环可以分为经过根节点和不经过根节点两种,不经过根节点的方案很好求,因为转移边固定,所以我们每次直接走不回到根节点的转移边,复杂度 (O(nm)) 然后我们考虑经过根节点,我们预处理出根节点走 (|t|) 步以内能到某一个节点的方案数,然后枚举从那一个节点回到根节点,复杂度 (O(nm))

代码:

#include<bits/stdc++.h>
#define inl inline
#define reg register

using namespace std;

namespace zzc
{
    typedef long long ll;
    
    const int maxn = 2005;
    const ll mod = 998244353;
    ll ch[maxn][26],nxt[maxn],f[maxn][maxn],mx[maxn];
    ll n,cnt,sum=1,ans,len;
    char s[maxn];

    void work()
    {
        scanf("%lld",&n);for(reg ll i=1;i<=n;i++) sum=sum*26%mod;
        scanf("%s",s+1);len=strlen(s+1);
        nxt[0]=-1;mx[0]=0;
        for(reg ll i=1;i<=len;i++)
        {
            ll j=nxt[i-1];
            while(j!=-1&&s[j+1]!=s[i]) j=nxt[j];
            nxt[i]=j+1;
        }
        for(reg int i=0;i<=len;i++) for(reg int c=0;c<26;c++)
        {
            if(s[i+1]-'a'==c) ch[i][c]=i+1;
            else ch[i][c]=(i==0?0:ch[nxt[i]][c]);
            if(ch[i][c]) mx[i]=c;
        }
        f[0][0]=1;
        for(reg ll i=0;i<n;i++) for(reg ll j=0;j<=len;j++) for(reg ll c=mx[j];c<26;c++) f[i+1][ch[j][c]]=(f[i+1][ch[j][c]]+f[i][j])%mod;
        for(reg ll i=0;i<=len;i++)
        {
            ll now=i;
            for(reg ll j=0;j<n;j++)
            {
                ans=(ans+1ll*(26-mx[now]-1)*f[n-j-1][i]%mod)%mod;
                now=ch[now][mx[now]];
                if(!now) break;
            }
            ans+=(now&&now==i);
        }
        printf("%lld
",(sum-ans+mod)%mod);
    }

}

int main()
{
    zzc::work();
    return 0;
}
原文地址:https://www.cnblogs.com/youth518/p/14465066.html