[HNOI2008]GT考试 - KMP + 矩阵快速幂
Description
求有多少个长度为n的0-9的数字串不包含一个给定的长度为m的数字串。
Solution
在kmp自动机上dp
40pts kmp + dp
考虑\(dp\),设\(f_{i,j}\)表示前\(i\)个字符,匹配到给定串的第\(j\)位的方案数
转移时枚举下一位放什么字符,然后用\(kmp\)处理出会匹配到哪一位,然后从\(f_{i,j}\)转移到\(f_{i,nxt}\)。需要注意的是,如果匹配到的下一位为\(m\),那么不需要进行转移。
最终答案为\(\sum \limits_{i=0}^{m-1} f[n][i]\)。
Code
#include <bits/stdc++.h>
using namespace std;
const int _ = 20 + 10;
const int __ = 1e6 + 10;
int N, M, mod, nxt[_];
char s[_];
int f[__][_];
inline void kmp() {
nxt[1] = 0;
for (int i = 2, j = 0; i <= M; ++i) {
while (j > 0 && s[i] != s[j + 1]) j = nxt[j];
if (s[i] == s[j + 1]) ++j;
nxt[i] = j;
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("GT.in", "r", stdin);
freopen("GT.out", "w", stdout);
#endif
scanf("%d%d%d", &N, &M, &mod);
scanf("%s", s + 1);
kmp();
f[0][0] = 1;
for (int i = 0; i < N; ++i) {
for (int j = 0; j <= M; ++j) {
for (int k = 0; k <= 9; ++k) {
int tmp = j;
while (tmp > 0 && k != s[tmp + 1] - '0') tmp = nxt[tmp];
if (k == s[tmp + 1] - '0') ++tmp;
if (tmp < M) f[i + 1][tmp] = (f[i + 1][tmp] + f[i][j]) % mod;
}
}
}
int ans = 0;
for (int i = 0; i < M; ++i) ans = (ans + f[N][i]) % mod;
printf("%d\n", ans);
return 0;
}
100pts 矩阵加速转移
实际上,并不需要每次枚举字母,我们可以预处理出从一个匹配长度加一个字符变成另一个匹配长度的方案数,设为\(g_{i,j}\)
则
\[f_{i,j}=\sum \limits_{k=0}^{m-1} f_{i-1,k} \times g_{k,j}
\]
这个式子显然是可以矩阵优化的,于是矩阵快速幂之后,就可以通过本题了。
Code
#include <bits/stdc++.h>
using namespace std;
const int _ = 23;
int N, M, mod, nxt[_], cnt[_][_];
char s[_];
struct Matrix {
int A[_][_];
Matrix() { memset(A, 0, sizeof(A)); }
Matrix operator*(const Matrix &b) const {
Matrix c;
memset(c.A, 0, sizeof(c.A));
for (int i = 0; i < M; ++i)
for (int j = 0; j < M; ++j)
for (int k = 0; k < M; ++k)
c.A[i][j] = (c.A[i][j] + A[i][k] * b.A[k][j] % mod) % mod;
return c;
}
} F, G;
inline void kmp() {
nxt[1] = 0;
for (int i = 2, j = 0; i <= M; ++i) {
while (j > 0 && s[i] != s[j + 1]) j = nxt[j];
if (s[i] == s[j + 1]) ++j;
nxt[i] = j;
}
for (int i = 0; i < M; ++i) {
for (int j = '0'; j <= '9'; ++j) {
int k = i;
while (k > 0 && j != s[k + 1]) k = nxt[k];
if (j == s[k + 1]) ++k;
if (k < M) ++cnt[i][k];
}
}
}
Matrix ksm(Matrix a, int b) {
Matrix ret;
for (int i = 0; i < M; ++i) ret.A[i][i] = 1;
for (; b; b >>= 1) {
if (b & 1) ret = ret * a;
a = a * a;
}
return ret;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("GT.in", "r", stdin);
freopen("GT.out", "w", stdout);
#endif
scanf("%d%d%d", &N, &M, &mod);
scanf("%s", s + 1);
kmp();
for (int i = 0; i < M; ++i)
for (int j = 0; j < M; ++j) G.A[i][j] = cnt[i][j];
F.A[0][0] = 1;
G = ksm(G, N);
F = F * G;
int ans = 0;
for (int i = 0; i < M; ++i) ans = (ans + F.A[0][i]) % mod;
printf("%d\n", ans);
return 0;
}
既然选择了远方,便只顾风雨兼程。