【题解】[HNOI2008]GT考试
链接
仍是关于 KMP 的状态机
先看一下状态机怎么运行的。
从 0 开始编号,第 size 位认为是个结束位(模板串的该位与任何字符均不匹配)
一次匹配执行步骤:
开始时,文本串 S 指针 i ,模板串 T 指针 j
1.【失配阶段】 j > 0 && S[i] != T[j] ,则 j = nx[j] (状态沿失配边转移),继续执行 1
2.【匹配阶段】 S[i] = T[j] ,则 j++ (状态右移一格)
3.i++
来看题:
在一个匹配过程中,想象把文本串遮住,你就只能看见模板串的指针每次匹配时,跳几次停下。
文本串的每一位都对应了模板串上的一个状态。所以对于任意满足条件的文本串,模板串的指针在不到达 size 位的情况下按上述规则跳
考虑 j ,枚举 S[i] ,就能根据上述规则知道 j 的答案可能贡献到哪了
模拟一下,发现可以用矩阵快速幂
矩阵模板get
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 21;
int N, M, mod, nx[MAXN];
char S[MAXN];
struct matrix {
int sz, a[MAXN][MAXN];
matrix(int _sz) { sz = _sz; memset(a, 0, sizeof(a)); }
inline matrix operator*(const matrix &b) const {
matrix res(M);
for (int i=0; i< sz; i++)
for (int j=0; j< sz; j++)
for (int k=0; k< sz; k++)
res.a[i][j] = (res.a[i][j]+a[i][k]*b.a[k][j]) % mod;
return res;
}
inline matrix operator^(int x) const {
matrix res(M), bas(M);
for (int i=0; i< sz; i++) res.a[i][i] = 1;
for (int i=0; i< sz; i++)
for (int j=0; j< sz; j++) bas.a[i][j] = a[i][j] % mod;
while (x) {
if (x&1) res = res * bas;
bas = bas * bas;
x >>= 1;
}
return res;
}
};
int main()
{
scanf("%d%d%d", &N, &M, &mod); scanf("%s", S);
nx[0] = 0, nx[1] = 0;
for (int i=1; i< M; i++) {
int j = nx[i];
while (j && S[i]!=S[j]) j = nx[j];
nx[i+1] = S[i]==S[j] ? j+1 : 0;
}
matrix e(M);
//for (int i=0; i< M; i++) e.a[nx[i]][i] = 9; // 憨批做法
//for (int i=1; i< M; i++) e.a[i][i-1]++;
for (int i=0; i< M; i++) { // 正解做法
for (int c='0'; c<='9'; c++) {
int j = i;
while (j && c!=S[j]) j = nx[j];
if (c==S[j]) j++;
e.a[j][i]++;
}
}
//for (int i=0; i< M; i++, puts(""))
// for (int j=0; j< M; j++) printf("%d ", e.a[i][j]);
e = e ^ N;
int ans = 0;
for (int i=0; i< M; i++) ans = (ans + e.a[i][0]) % mod;
printf("%d", ans);
}

状态机的运行
浙公网安备 33010602011771号