[HNOI2008]GT考试
[HNOI2008]GT考试
以前太菜, 一看到考试两字就劝退...
题意: 求有多少字符集为数字, 长度为n的字符串, 要求字符串中不能出现特定字串
不得不说这是一道字符串好题, 计数想\(dp\), \(f_{i,j}\)表示长度为\(i\), 匹配到\(j\)位方案数. 转移方程较为复杂, 考虑到\(f_{i,j}\)不止可以从\(f_{i-1,j-1}\)转移过来, 因为失配时可以像\(kmp\)那样找到最长匹配前缀, 预处理出数组\(g_{i,j}\)表示是否可以添加一个字符从匹配长度\(i\)到匹配长度\(j\). 这个\(kmp\)即可, 转移方程: \(f_{i,j} = \sum_{k=0}^{m-1}f_{i-1,k}*g_{k,j}\) 像不像矩阵乘法, 没错它就是. 矩阵快速幂一发救星了
\(warning\): 以下部分为本人\(yy\)
突然想起了在图论中也有矩阵乘法维护走k步的方案数, 现在想想, 图论和动态规划好像有着某些联系, 我们可以把图论的边想象成动态规划的转移, 点化为状态, 这可能也是\(floyd\)可以解决最短路问题的本质吧
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
template <typename T>
void read(T &x) {
x = 0; bool f = 0;
char c = getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
for (;isdigit(c);c=getchar()) x=x*10+(c^48);
if (f) x=-x;
}
template <typename T>
void write(T x) {
if (x < 0) putchar('-'), x = -x;
if (x >= 10) write(x / 10);
putchar('0' + x % 10);
}
int n, m, P;
const int N = 23;
struct Matrix {
int f[N][N];
Matrix () {
memset(f, 0, sizeof(f));
}
Matrix operator * (Matrix y) {
Matrix tmp;
for (int i = 0;i < m; i++)
for (int j = 0;j < m; j++)
for (int k = 0;k < m; k++)
tmp.f[i][j] = (tmp.f[i][j] + f[i][k] * y.f[k][j]) % P;
return tmp;
}
}I, M;
char s[N];
int nxt[N];
void prework() {
nxt[1] = 0; int j = 0;
for (int i = 2; i <= m; i++) {
while (j && s[j+1] != s[i]) j = nxt[j];
if (s[j+1] == s[i]) j++;
nxt[i] = j;
}
for (int i = 0;i < m; i++) {
for (int j = '0';j <= '9'; j++) {
int k = i;
while (k && s[k+1] != j) k = nxt[k];
if (s[k+1] == j) k++;
++M.f[i][k];
}
}
}
Matrix fpw(Matrix x, ll mi) {
Matrix res = I;
while (mi) {
if (mi & 1) res = res * x;
x = x * x;
mi >>= 1;
}
return res;
}
int main() {
read(n), read(m), read(P);
scanf ("%s", s + 1); prework();
for (int i = 0;i < m; i++) I.f[i][i] = 1;
Matrix ans = fpw(M, n);
ll res = 0;
for (int i = 0;i < m; i++) res += ans.f[0][i];
cout << res % P << endl;
return 0;
}