zoukankan      html  css  js  c++  java
  • bzoj1009 [HNOI2008]GT考试——KMP+矩阵快速幂优化DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1009

    字符串计数DP问题啊...连题解都看了好多好久才明白,别提自己想出来的蒟蒻我...

    首先要设计一个不太好想的状态:f[i][j]表示大串上到第 i 位时有小串前 j 位的后缀,且不包含整个小串的方案数;

    也就是如果小串是 12312 , f[5][3] 表示目前大串的情况是 **123... ;

    这个状态要从 i 转移到 i+1 ,还需要一个帮助它的数组 a,a[i][j]表示在长度为 i 的后缀后面加一个数字能变成长度为 j 的后缀的方案数;

    也就是说,对于 12312,从0到4的 a 数组应该如下:

    9 1 0 0 0

    8 1 1 0 0

    8 1 0 1 0

    9 0 0 0 1

    8 1 0 0 0

    a 数组的定义可以联想到 kmp 算法,事实上它就是通过 kmp 算法的 nxt 数组求得;

    于是就可以得到转移方程:f[i][j] = ∑(0<=k<m) f[i-1][k] * a[k][j]

    然后发现对于每一步,进行的转移都是相同的;

    所以可以用矩阵快速幂来优化,转移矩阵就是 a 数组;

    看了好多好多博客才明白...

    这篇博客写得很好:https://blog.csdn.net/loi_dqs/article/details/50897662

    尤其是代码真的简洁!所以模仿着写了。

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    int n,m,mod,nxt[25],sum;
    char s[25];
    struct Matrix{
        int n,m,a[25][25];
        Matrix(int x=0,int y=0):n(x),m(y) {memset(a,0,sizeof a);}
        void init()
        {
            for(int i=0;i<n;i++)a[i][i]=1;//0 ~ n-1
        }
        Matrix operator * (const Matrix &y) const
        {
            Matrix x(n,y.m);
            for(int i=0;i<n;i++)//从0到n-1 
                for(int k=0;k<m;k++)
                    for(int j=0;j<y.m;j++)
                        (x.a[i][j]+=(ll)a[i][k]*y.a[k][j]%mod)%=mod;
            return x;
        }
    };
    void getnxt()
    {
        nxt[0]=nxt[1]=0;//第0位有字符,但含义是无匹配 
        for(int i=1;i<m;i++)
        {
    //        int k=i;
            int k=nxt[i];
            while(s[i]!=s[k]&&k)k=nxt[k];
            nxt[i+1]=(s[i]==s[k])?k+1:0;//前一位的nxt冒进一位,对应下面从0开始的字符串匹配 
        }
    }
    Matrix pw(Matrix x,int k)
    {
        Matrix ret(x.n,x.m); ret.init();
        for(;k;k>>=1,x=x*x)
            if(k&1)ret=ret*x;
        return ret;
    }
    int main()
    {
        scanf("%d%d%d%s",&n,&m,&mod,&s);
        getnxt();
        Matrix f(m,m);
        for(int i=0;i<m;i++)
            for(int j='0';j<='9';j++)//第i位上填j 
            {
                int k=i;//已经有长度为i的前缀,而k对应字符串上i的后一位 
                while(k&&s[k]!=j)k=nxt[k];//冒进一位的nxt,表示给i后一位进行匹配 
                if(s[k]==j)k++;//匹配到了第k位,也就是有了k+1长度的前缀
                if(k!=m)f.a[i][k]++; 
            }
        Matrix fn=pw(f,n);
        Matrix ans(1,m);
        ans.a[0][0]=1; ans=ans*fn;
        for(int i=0;i<m;i++)
            (sum+=ans.a[0][i])%=mod;
        printf("%d",sum);
        return 0;
    }
  • 相关阅读:
    my15_ mysql binlog格式从mixed修改为row格式
    my14_mysql指定时间恢复之模拟从库
    my13_mysql xtrabackup备份的时间点
    必知必会的图论算法
    leetcde37. Sudoku Solver
    leetcode36. Valid Sudoku
    leetcode52. N-Queens II
    leetcode51. N-Queens
    First Missing Positive
    Maximum Gap
  • 原文地址:https://www.cnblogs.com/Zinn/p/9254148.html
Copyright © 2011-2022 走看看