1. 程式人生 > >【KMP+矩陣】BZOJ

【KMP+矩陣】BZOJ

題意:

給出一串長度為m的不吉利數字,要構造出一個長度為n的數字串不包含這個不吉利的數字,問構造方案數是多少。

(m<=20,n<=1e9)

題解:

設陣列dp[i][j]表示當前匹配到i個字元,新增一個字元變成匹配為j個字元的方案數。這裡的i和j可以看成狀態。

對dp陣列做n次的快速冪,表示新增n個字元之後的狀態。然後累加dp[0][i]就是答案。

#include<bits/stdc++.h>
using namespace std;
const int N=50;
int n,m,mod;
char s[N];
int nex[N];
void getnex(){
    int i=0,j=-1;
    nex[i]=-1;
    while(i<m){
        if(s[i]==s[j]||j==-1) i++,j++,nex[i]=j;
        else j=nex[j];
    }
}
void mul(int a[N][N],int b[N][N]){
    int c[N][N];
    memset(c,0,sizeof(c));
    for(int i=0;i<m;i++)
        for(int j=0;j<m;j++)
            for(int k=0;k<m;k++)
                c[i][j]=(c[i][j]+a[i][k]*b[k][j])%mod;
    memcpy(a,c,sizeof(c));
}
void mpow(int a[N][N],int b){
    int c[N][N];
    for(int i=0;i<m;i++)
        for(int j=0;j<m;j++)
            c[i][j]=(i==j);
    for(int i=b;i;i>>=1,mul(a,a))
        if(i&1) mul(c,a);
    memcpy(a,c,sizeof(c));
}
int a[N][N];
int main(){
    scanf("%d%d%d",&n,&m,&mod);
    scanf("%s",s);
    getnex();
    for(int i=0;i<m;i++){
        for(int j=0;j<=9;j++){
            int tmp=i;
            while(1){
                if(j==s[tmp]-'0'||tmp==-1){
                    if(tmp==m-1) break;
                    a[i][tmp+1]++;
                    break;
                }
                tmp=nex[tmp];
            }
        }
    }
    mpow(a,n);
    int ans=0;
    for(int i=0;i<m;i++)
        ans=(ans+a[0][i])%mod;
    printf("%d\n",ans);
}