题目描述
阿申准备报名参加 GT 考试,准考证号为 NN 位数X_1,X_2…X_n(0\le X_i\le9)X1,X2…Xn(0≤Xi≤9),他不希望准考证号上出现不吉利的数字。 他的不吉利数字A_1,A_2…A_m(0\le A_i\le 9)A1,A2…Am(0≤Ai≤9) 有 MM 位,不出现是指 X_1,X_2…X_nX1,X2…Xn 中没有恰好一段等于 A_1,A_2…A_mA1,A2…Am,A_1A1 和X_1X1 可以为 00
输入格式
第一行输入N,M,K.接下来一行输入M位的数。
输出格式
阿申想知道不出现不吉利数字的号码有多少种,输出模 KK 取余的结果。
输入数据 1
4 3 100
111
Copy
输出数据 1
81
Copy
提示
N\leq10^9,M\leq20,K\leq1000N≤109,M≤20,K≤1000
代码:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e9 + 10, M = 30;
int n, m, P;
char s[M];
int ne[M], c[M][M], f[M][M];
void mult(int a[][M], int b[][M]) {
static int res[M][M];
memset(res, 0, sizeof res);
for (int i = 0; i < m; i++)
for (int j = 0; j < m; j++)
for (int k = 0; k < m; k++)
// 注意这里的乘法要写为b * a,因为我们要求mult(f, c),实际上是矩阵c乘以矩阵f
res[i][j] = (res[i][j] + b[i][k] * a[k][j]) % P;
for (int i = 0; i < m; i++)
for (int j = 0; j < m; j++) a[i][j] = res[i][j];
}
void fast_pow(int n) {
// 注意这里我们将f向量横向扩展为了f矩阵,最终答案只需考虑第0列即可
f[0][0] = 1;
while (n) {
if (n & 1) mult(f, c);
n >>= 1;
mult(c, c);
}
}
int main() {
scanf("%d%d%d", &n, &m, &P);
scanf("%s", s + 1);
for (int i = 2, j = 0; i <= m; i++) {
while (j && s[i] != s[j + 1]) j = ne[j];
if (s[i] == s[j + 1]) j++;
ne[i] = j;
}
for (int j = 0; j < m; j++)
for (char ch = '0'; ch <= '9'; ch++) {
int k = j;
while (k && s[k + 1] != ch) k = ne[k];
if (s[k + 1] == ch) k++;
if (k < m) c[k][j]++;
}
fast_pow(n);
int res = 0;
for (int i = 0; i < m; i++) res = (res + f[i][0]) % P;
printf("%d\n", res);
}