题目:阿申准备报名参加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;
}