【题解】[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);
}
posted @ 2021-02-24 20:07  zrkc  阅读(56)  评论(0)    收藏  举报