【BZOJ1009】[HNOI2008]GT考试
题面
题解
设(f_{i,j})表示长串匹配到(i),短串匹配到(j)的方案数。
那么我们如何转移呢?
很显然,我们每次匹配时添加字母,可能会失配或者重新匹配到原串的一个地方。
我们预处理出一个矩阵(g_{i,j})表示短串第(i)个字符到第(j)的方案数,
那么转移(f_{i,j}=sum f_{i,k} imes g_{k,j})。
这个用矩阵快速幂优化一下即可。
最后答案
[Ans=sum_{i=0}^{M-1} f_{N,i}
]
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
inline int gi() {
register int data = 0, w = 1;
register char ch = 0;
while (ch != '-' && (ch > '9' || ch < '0')) ch = getchar();
if (ch == '-') w = -1 , ch = getchar();
while (ch >= '0' && ch <= '9') data = data * 10 + (ch ^ 48), ch = getchar();
return w * data;
}
#define MAX_M 50
char s[MAX_M];
int N, M, K;
int P[MAX_M], g[MAX_M][MAX_M];
void get_next() {
P[1] = 0;
int j = 0;
for (int i = 2; i <= M; i++) {
while (j > 0 && s[j + 1] != s[i]) j = P[j];
if (s[j + 1] == s[i]) ++j;
P[i] = j;
}
}
struct Matrix {
int s[30][30];
void init() {
memset(s, 0, sizeof(s));
for(int i = 0; i < M; ++i) s[i][i] = 1;
}
void clear() { memset(s, 0, sizeof(s)); }
} G;
Matrix operator * (Matrix a, Matrix b) {
Matrix res;
res.clear();
for (int i = 0; i < M; ++i)
for (int j = 0; j < M; ++j)
for (int k = 0; k < M; ++k)
(res.s[i][j] += a.s[i][k] * b.s[k][j] % K) %= K;
return res;
}
Matrix fpow(Matrix a, int b) {
Matrix s;
s.init();
while (b) {
if(b & 1) s = s * a;
a = a * a;
b >>= 1;
}
return s;
}
int main () {
N = gi(); M = gi(); K = gi();
scanf("%s", s + 1);
get_next();
for (int j = '0'; j <= '9'; j++) {
for (int k = 0; k < M; k++) {
int t = k;
while (t > 0 && s[t + 1] != j) t = P[t];
if (j == s[t + 1]) ++t;
G.s[k][t]++;
}
}
G = fpow(G, N);
int ans = 0;
for (int i = 0; i < M; i++) ans = (ans + G.s[0][i]) % K;
printf("%d
", ans);
return 0;
}