BZOJ1009 HNOI2008 GT考试 一般DP+矩阵乘法+KMP

题意:给定一个长度为M的字符串A,求长度为N的字符串中,子串中不包含A的字符串的数量,其中字符串仅由‘0’-‘9’组成。
题解:设f[i][j]=长度为i最后几位能匹配A的前j个字符的字符串种数,那么每往后添加一个字符,能转移到的位置通过KMP的Next数组很轻松就能找到。那么我们就能构造出来一个矩阵,a[i][j]=1表示可以通过在A[i]后面加一个字符,使得A[1]-A[j]成为原有字符串的子串。快速幂优化后答案就是f[N][0]+……+f[N][M-1]

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long

const int MAXN=20+2;
int N,M,Fail[MAXN];
char A[MAXN];
ll K,Ans;
struct MATRIX{
    ll m[MAXN][MAXN];
    int l,r;

    void Init(){
        l=r=0;
        memset(m,0,sizeof(m));
    }
    MATRIX operator*(MATRIX a){
        MATRIX ret;
        ret.Init();
        ret.l=l,ret.r=a.r;

        for(int i=0;i<ret.l;i++)
        for(int j=0;j<ret.r;j++)
        for(int k=0;k<r;k++)
            ret.m[i][j]=(ret.m[i][j]+m[i][k]*a.m[k][j]%K)%K;
        return ret;
    }
    MATRIX operator^(int x){
        MATRIX t,a;

        a.l=t.l=l,a.r=t.r=r;
        memcpy(a.m,m,sizeof(a.m));

        if(x&1) memcpy(t.m,m,sizeof(t.m));
        else
            for(int i=0;i<l;i++)
            for(int j=0;j<r;j++)
                t.m[i][j]=(i==j);

        while(x>>=1){
            a=a*a;
            if(x&1) t=a*t;
        }
        return t;
    }
}T;

int main(){
    cin >> N >> M >> K;
    scanf("%s",A+1);

    for(int i=2,j=0;i<=M;i++){
        while(j && A[j+1]!=A[i]) j=Fail[j];
        if(A[j+1]==A[i]) j++;
        Fail[i]=j;
    }

    for(int i=0;i<M;i++)
        for(int j=0;j<=9;j++){
            int p=i;
            while(p && A[p+1]-'0'!=j) p=Fail[p];
            if(A[p+1]-'0'==j) p++;

            if(p!=M) T.m[p][i]++;
        }
    T.l=T.r=M;

    T=T^N;
    for(int i=0;i<M;i++) Ans=(Ans+T.m[i][0])%K;
    cout << Ans << endl;

    return 0;
}
View Code

 

posted @ 2017-02-26 02:02  WDZRMPCBIT  阅读(152)  评论(0编辑  收藏  举报