BZOJ 1009. [HNOI2008]GT考试
显然有一个DP方程 $dp[i][j]$ 表示到第 $i$ 位已经末尾匹配了 $j$ 位的方案数。
暴力的话就枚举下一位放啥,看放完之后又匹配了多少。
这里可以引入一个 $f[i][j]$ 数组表示从不吉利数字当前匹配了 $i$ 位,加上一个字符能匹配 $j$ 位的方案数。
这一部分可以用kmp得到。
然后dp的转移方程即为 $dp[i][j] = \sum dp[i - 1][k] \times f[k][j]$
答案为 $\sum_{i = 0}^{m - 1} dp[n][i]$
#include <bits/stdc++.h> const int N = 22; int MOD, n, m; char s[N]; int ne[N]; struct Mat { int mat[25][25]; Mat() { memset(mat, 0, sizeof mat); } Mat operator * (const Mat &rhs) const { Mat c; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) for (int k = 0; k < n; k++) (c.mat[i][j] += mat[i][k] * rhs.mat[k][j]) %= MOD; return c; } }; Mat qp(Mat ans, Mat a, int b) { while (b) { if (b & 1) ans = ans * a; a = a * a; b >>= 1; } return ans; } void kmp() { int i = 0, j = ne[0] = -1; while (i < n) { while (j != -1 && s[i] != s[j]) j = ne[j]; ne[++i] = ++j; } } int main() { scanf("%d%d%d", &m, &n, &MOD); scanf("%s", s); kmp(); Mat b; for (int i = 0; i < n; i++) { for (int j = 0; j < 10; j++) { int k = i; while (k != -1 && s[k] != j + '0') k = ne[k]; k++; if (k < n) b.mat[i][k]++; } } Mat a; a.mat[0][0] = 1; a = qp(a, b, m); int ans = 0; for (int i = 0; i < n; i++) ans += a.mat[0][i]; ans %= MOD; printf("%d\n", ans); return 0; }