题目:阿申准备报名参加GT考试,准考证号为n位数\(X1X2...Xn,\)他不希望准考证号上出现不吉利的数字。他的不吉利数字\(A1A2...Am\)有m位,不出现是指\(X1X2...Xn\)中没有恰好一段等于\(A1A2...Am\)\(A1\)\(X1\)可以为0。

\(输入格式\)
第一行输入\(n,m,K\)
接下来一行输入m位的不吉利数字

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

分析
我们定义f[i, j]为所有长度为i,且不包含不吉利字符串,且末尾部分与不吉利字符串的前缀匹配的最大长度是j的所有字符串的方案集合。

我们可以得到\(f[i, j] = \sum\limits_{k = 0}^{m - 1}f[i - 1][k] * a[k][j]\),a[k][j]表示已匹配k位,再匹配一个字符,就是j位的方案数,那么我们发现每一层的\((f[i, 0], f[i, 1], f[i, 2], ...f[i, n - 1], f[i, n])\)都会乘以固定的a[][],我们可以预先处理出这个a矩阵,使用KMP算法

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

const int N = 25;

int n, m, mod;

char str[N];
int ne[N];

int a[N][N];

void mul(int c[][N], int a[][N], int b[][N])
{
    static int t[N][N];
    memset(t, 0, sizeof t);
    
    for(int i = 0; i < m; ++i)
        for(int j = 0; j < m; ++j)
            for(int k = 0; k < m; ++k)
                t[i][j] = (t[i][j] + a[i][k] * b[k][j]) % mod;
    
    memcpy(c, t, sizeof t);
}

int qmi(int k)
{
    int f[N][N] = {1};
    
    while(k)
    {
        if(k & 1) mul(f, f, a);
        mul(a, a, a);
        k >>= 1;
    }
    
    int res = 0;
    for(int i = 0; i < m; ++i) res = (res + f[0][i]) % mod;
    return res;
}

int main()
{
    //准考证号和不吉利数字位数
    cin >> n >> m >> mod;
    cin >> str + 1;
    
    //求不吉利字符串的ne数组
    for(int i = 2, j = 0; i <= m; ++i)
    {
        while(j && str[j + 1] != str[i]) j = ne[j];
        if(str[j + 1] == str[i]) ++j;
        ne[i] = j;
    }
    
    
    //预处理出A[i][j]
    
    for(int j = 0; j < m; ++j)
        //枚举下一位字符
        for(int c = '0'; c <= '9'; ++c)
        {
            //已经匹配长度
            int k = j;
            //寻找匹配的长度
            while(k && str[k + 1] != c) k = ne[k];
            //找到
            if(str[k + 1] == c) ++k;
            //k < m防止越界
            if(k < m) ++a[j][k];
        }
    
    //F[n] = F[0] * A ^ n
    
    cout << qmi(n) <<endl;
    
    
    return 0;
}