1009: [HNOI2008]GT考试
1009: [HNOI2008]GT考试
https://www.lydsy.com/JudgeOnline/problem.php?id=1009
分析:
f[i][j]表示第一个字符串到i,与第二个匹配了j个的方案数。新加一个字符,如果第一个字符串仍与第二个一样,那么转移到f[i+1][j+1],否则,转移到f[i+1[k],k是不确定的。预处理g[i][j]表示两个字符串匹配了i,然后增加一个字符后,匹配的位数变成j后的方案数。
那么有转移方程:$dp[i][j]=\sum_{0<=k<=m-1}dp[i-1][k] \times g[k][j]$
然后发现n特别大,但是每个i转移只有20-1(等于m了,就不用记录了)个,所以可以矩阵快速幂。
代码:
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<iostream> #include<cctype> #include<set> #include<vector> #include<queue> #include<map> using namespace std; typedef long long LL; inline int read() { int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; } const int N = 100; int p[N], g[N][N]; char s[N]; int n, m, mod; struct Matrix{ int a[N][N]; void init() { for (int i=0; i<m; ++i) a[i][i] = 1; } void Clear() { memset(a, 0, sizeof(a)); } }A; Matrix mul(Matrix A, Matrix B) { Matrix C; C.Clear(); for (int k=0; k<m; ++k) for (int i=0; i<m; ++i) for (int j=0; j<m; ++j) C.a[i][j] = (C.a[i][j] + 1ll * A.a[i][k] * B.a[k][j] % mod) % mod; return C; } Matrix ksm(Matrix a,int b) { Matrix res; res.Clear(); res.init(); while (b) { if (b & 1) res = mul(res, a); a = mul(a, a); b >>= 1; } return res; } void KMP() { p[1] = 0; for (int i=2; i<=m; ++i) { int j = p[i - 1]; while (j && s[j + 1] != s[i]) j = p[j]; if (s[j + 1] == s[i]) j ++; p[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 = p[k]; if (s[k + 1] == j) g[i][k + 1] ++; else g[i][0] ++; } } } int main() { n = read(), m = read(), mod = read(); scanf("%s",s+1); KMP(); for (int i=0; i<=m; ++i) for (int j=0; j<=m; ++j) A.a[i][j] = g[i][j]; A = ksm(A, n); int Ans = 0; for (int i=0; i<m; ++i) Ans = (Ans + A.a[0][i]) % mod; cout << Ans; return 0; }