Luogu P10581 蓝桥杯2024国A 重复的串 题解 [ 蓝 ] [ KMP ] [ 动态规划 ] [ 矩阵加速 ]

重复的串:KMP + dp 的板子题。

暴力 dp

设计 \(dp_{k,i,j}\) 表示主串匹配到第 \(i\) 位,模式串有 \(j\) 位已匹配完成,目前已完成 \(k\) 次匹配的方案数。

不难写出暴力的填表法转移式子,发现是平方级别的转移,所以使用填表法不行,我们尝试刷表法。

\(dp_{k,i,j}\) 要转移去的地方显然是第 \(i+1\) 位,于是我们要观察 \(j\) 里哪些是合法的,很容易就想到用 KMP 加速这个过程,因为 KMP 做的也是匹配到某一位后,求出下一位的最大匹配长度。

假设 \(m\) 为模式串的长度。

对于 \(dp_{k,i,j}\) 我们枚举第 \(i+1\) 位的字符是什么,依次和当前需要匹配的字符进行比较,然后用 KMP 的 next 数组求出下一位的最大匹配长度 \(l\)

  • 当最大匹配长度为 \(m\) 时,这时候 \(k\) 就要 \(+1\),所以 \(dp_{k+1,i+1,next_l} \gets dp_{k+1,i+1,next_l}+dp_{k,i,j}\)
  • 当最大匹配长度小于 \(m\) 时,直接转移即可,\(dp_{k,i+1,l} \gets dp_{k,i+1,l}+dp_{k,i,j}\)

时间复杂度是 \(O(k|\sum|n)\) 的,显然不可过。

矩阵优化

发现当值域非常大的 \(i\) 确定时,剩下两维的数量很小,只有 \(90\) 个值左右,考虑矩阵优化 dp。

我们把 \(i\) 这一位提取到前面来,作为快速幂的幂次即可。

构造矩阵有点难描述,看代码吧,大体就是做一遍上面的那个转移,注意细节问题就好了。

另外,当 \(j=m\) 时,不能进行转移。

时间复杂度 \(O((k|\sum|)^3 \log n)\)

关于完整匹配

本题中,完整匹配后可以跳到 \(next\) 处,是因为这样统计方案不会重复。而其他题中,有时候完整匹配是不能直接跳回 \(next\) 的,因为完全匹配后和 \(next\) 的情况可能是不同的。这时候就要对 \(next\) 进行转移,其实就不用特判 \(next\) 就行了,下次它会自己跳回去自己的 \(next\) 的。

代码

#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pi;
const ll mod=998244353;
int n,ne[35],m;
char c[35];
struct mat{
    ll a[105][105];
    mat(){memset(a,0,sizeof(a));}
    mat operator*(const mat &t)const{
        mat res;
        for(int i=0;i<=100;i++)
        {
            for(int k=0;k<=100;k++)
            {
                for(int j=0;j<=100;j++)
                {
                    res.a[i][j]=(res.a[i][j]+a[i][k]*t.a[k][j])%mod;
                }
            }
        }
        return res;
    }
};
void construct_dp(mat &dp)
{
    for(int lv=0;lv<=2;lv++)
    {
        int ns=1+lv*(m+1);
        for(int i=ns;i<ns+m;i++)
        {
            for(int j=0;j<26;j++)
            {
                char nc=('a'+j);
                int now=i-ns;
                while(now&&nc!=c[now+1])now=ne[now];
                if(nc==c[now+1])now++;
                if(now==m)
                {
                    if(lv==2)continue;
                    now=ne[now];
                    dp.a[i][ns+m+1+now]=(dp.a[i][ns+m+1+now]+1)%mod;
                }
                else dp.a[i][ns+now]=(dp.a[i][ns+now]+1)%mod;
            }
        }    
    }
}
void construct_init(mat &s){s.a[1][1]=1;}
mat qpow(mat a,ll b)
{
    mat res;
    for(int i=0;i<=100;i++)res.a[i][i]=1;
    while(b)
    {
        if(b&1)res=res*a;
        a=a*a;
        b>>=1;
    }
    return res;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>c+1>>n;
    m=strlen(c+1);
    int now=0;
    for(int i=2;i<=m;i++)
    {
        while(now&&c[now+1]!=c[i])now=ne[now];
        if(c[now+1]==c[i])now++;
        ne[i]=now;
    }
    mat dp;
    construct_dp(dp);
    mat s;
    construct_init(s);
    dp=s*qpow(dp,n);
    ll ans=0;
    for(int i=1+2*(m+1);i<1+3*(m+1);i++)ans=(ans+dp.a[1][i])%mod;
    cout<<ans;
    return 0;
}
posted @ 2024-12-10 01:27  KS_Fszha  阅读(70)  评论(0)    收藏  举报