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\n", ans);

    return 0;
}
posted @ 2016-01-03 23:05  Armeria  阅读(141)  评论(0编辑  收藏  举报