• BZOJ 1009 KMP思想 + DP + 矩阵快速幂


    第一次用MarkDown和LaTex,写得有点丑……

    本题的坑爹历程给了我一个血的教训:没有真正搞清楚做法之前,不要瞎BB地写题解。不然会造成深陷坑中的严重后果

    题意简述:给定一个字符串s,求出长度为n的不含字串s的字符串t的数量。

    这道题是一个非常经典的模型,DP之:
     f[i][j]为前i个t字符,匹配到s的第 j位(强制选 i)的方案数,则有

    ans=Σ( f[n][k] | 0k<m )

    接下来考虑 f[i][j]的转移:
    由于我们是要统计数量,所以我们可以枚举第 i+1位是几。然后自然地,应该转移到(这里亦即贡献到)f[i+1][pos],其中pos为加上这个字符之后,当前串匹配到的位置。

    然后我们发现,每一轮i=>i+1的转移都是一样的。对于这样的“无脑”递推,我们可以构造矩阵,然后使用矩阵快速幂来加速。如何构造矩阵呢?我们考虑矩阵元素M[i][j]的意义。它会与当前向量的第j个维度相乘,然后贡献到下一个向量的第i个维度。因此,如果有f[i+1][pos] += f[i][j],就应把M[pos][j]加1。不难发现,处理好这个矩阵之后,它的第0列其实就是初始向量。所以,只需做n次矩阵乘法即可。

    代码略丑:

    // BZOJ 1009 KMP+Matrix
    
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
     const int M=25, N=M;
    
     #define rep(i,a,b) for (int i=a; i<=b; i++)
     #define dep(i,a,b) for (int i=a; i>=b; i--)
     #define read(x) scanf("%d", &x)
     #define fill(a,x) memset(a, x, sizeof(a))
    
     int mod;
     struct Matrix {
        int n, m, a[N][M];
    
        void init(int n, int m) {
            this->n=n;
            this->m=m;
            fill(a, 0);
        }
    
        Matrix operator * (const Matrix B) const {  
            Matrix ret;
            ret.init(B.n, B.n);
            rep(i,0,n-1) rep(j,0,n-1) rep(k,0,n-1) ret.a[i][j]=(ret.a[i][j]+a[i][k]*B.a[k][j])%mod;
            return ret; 
        }
    
        Matrix operator ^ (const int k) const {
            Matrix ret, t=*this;
            ret.n=n; ret.m=m;
            int tk=k;
            fill(ret.a, 0);
            rep(i,0,ret.n-1) ret.a[i][i]=1;
            while (tk) {
                if (tk&1) ret=ret*t;
                t=t*t;
                tk>>=1;     
            }
            return ret;
        }
     } A;
    
     int f[M], n, m;
     char st[M];
    
     void get_fail() {
        f[0]=f[1]=0;
        int j=0;
        rep(i,2,m) {
            while (j && st[j+1]!=st[i]) j=f[j];
            if (st[j+1]==st[i]) j++;
            f[i]=j;
        }
     }
    
    int main()
    {
        read(n); read(m); read(mod);
        scanf("%s", st+1);
        A.init(m, m);
    
        get_fail();
    
        rep(i,0,m-1) 
          rep(j,0,9) {
            int pos=i; 
            while (pos && st[pos+1]-'0'!=j) pos=f[pos];
            if (st[pos+1]-'0'==j) pos++;
            if (pos!=m) A.a[pos][i]=(A.a[pos][i]+1)%mod;
          }
    
        A=A^n;
        int ans=0;
        rep(i,0,m-1) ans=(ans+A.a[i][0])%mod;
        printf("%d
    ", ans);
    
        return 0;
    }
    
  • 相关阅读:
    C++中的派生类相关内容,结构体、共同体内容
    window查看端口以及telnet的使用
    java中long型转换为int
    C/C++数组初始化全为0
    linux中的一些指令 find
    bat相关知识
    bat设置开机自启动
    for循环语句及批量创建用户!
    Shell函数!
    case语句!
  • 原文地址:https://www.cnblogs.com/yearwhk/p/5119860.html
Copyright © 2020-2023  润新知