BZOJ_1009_[HNOI2008]GT考试_KMP+矩阵乘法

BZOJ_1009_[HNOI2008]GT考试_KMP+矩阵乘法

Description

  阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字。
他的不吉利数学A1A2...Am(0<=Ai<=9)有M位,不出现是指X1X2...Xn中没有恰好一段等于A1A2...Am. A1和X1可以为0

Input

  第一行输入N,M,K.接下来一行输入M位的数。 N<=10^9,M<=20,K<=1000

Output

  阿申想知道不出现不吉利数字的号码有多少种,输出模K取余的结果.

Sample Input

4 3 100
111

Sample Output

81

设F[i][j]表示长度为i的串,已经和模式串匹配到第j位有多少种方案。
然后枚举第i位是啥,转移就是F[i-1][k]->F[i][j],k为加入第i位前在模式串中匹配到哪一位。
其中(k,j)可以用KMP求出(其实暴力找下一位匹配的位置就可以)
同时要保证不能匹配到最后一位,即j不能为m。
然后用矩阵乘法加速这个转移过程。
 
代码:
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
int mod,n,m,nxt[25];
char s[25];
struct Mat {
    int v[21][21];
    Mat(){memset(v,0,sizeof(v));}
    Mat operator*(const Mat &x)const {
        Mat re;int i,j,k;
        for(i=0;i<=m;i++) {
            for(j=0;j<=m;j++) {
                for(k=0;k<=m;k++) {
                    (re.v[i][j]+=v[i][k]*x.v[k][j]%mod)%=mod;
                }
            }
        }
        return re;
    }
};
void print(Mat w) {
    int i,j;
    for(i=0;i<=m;i++) {
        for(j=0;j<=m;j++) {
            printf("%d ",w.v[i][j]);
        }puts("");
    }
}
Mat qp(Mat x,int y) {
    Mat I;
    int i;
    for(i=0;i<=m;i++) I.v[i][i]=1;
    while(y) {
        if(y&1) I=I*x;
        x=x*x;
        y>>=1;
    }
    return I;
}
void getnxt() {
    int i=0,j=-1;
    nxt[0]=-1;
    while(i<m) {
        if(j==-1||s[i]==s[j]) nxt[++i]=++j;
        else j=nxt[j];
    }
}
int main() {
    scanf("%d%d%d%s",&n,&m,&mod,s);
    int i,j,k;
    getnxt();
    Mat x,ans;
    for(i=0;i<m;i++) {
        for(j=0;j<=9;j++) {
            k=i;
            while(k!=-1&&s[k]-'0'!=j) k=nxt[k];
            x.v[i][k+1]++;
        }
    }
    ans.v[0][0]=1;
    ans=ans*qp(x,n);
    int sum=0;
    for(i=0;i<m;i++) sum=(sum+ans.v[0][i])%mod;
    printf("%d
",sum);
}
原文地址:https://www.cnblogs.com/suika/p/8996226.html