穿梭时间的画面的钟 从反方向 开始移动|

tmjyh09

园龄:3年2个月粉丝:1关注:3

P3193 [HNOI2008]GT考试

先考虑朴素的 dp。

由于涉及到匹配问题,只有一个串,考虑 kmp。

状态表示

fi,j 表示长度为 i 的字符串,与不吉利串的匹配长度为 j 的总方案数。

状态转移

枚举待添加的字符 c,然后计算添加后的最大匹配长度 k(这里显然 kj),所以

fi+1,kfi+1,k+fi,j

边界条件

f0,0=1

优化

这样 dp 的时间复杂度是 O(nm) 的,在本题会超时。

再仔细地分析转移方程,如果我们设 aj,k 表示从匹配长度为 j 添加 1 个字符,匹配长度变为 k 的方案数,此时状态转移方程变为

fi+1,k=j=0m1fi,j×aj,k

这个方程特别像 矩阵乘法,而且这个矩阵 A 是固定不变的,可以预处理出来。所以考虑矩阵快速幂优化。

就设行向量 Fi=[fi,0fi,1fi,m1]

然后很容易可以发现 Fi+1=Fi×A

所以 Fn=F0×An

时间复杂度

预处理 A 复杂度 O(m),矩阵快速幂 O(logn)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;
const int N = 25;
int n, m, P;
char str[N];
int ne[N];
int a[N][N];
void mul(int c[][N], int a[][N], int b[][N])
{
static int t[N][N];
memset(t, 0, sizeof t);
for (int i = 0; i < m; i ++ )
for (int j = 0; j < m; j ++ )
for (int k = 0; k < m; k ++ )
t[i][j] = (t[i][j] + a[i][k] * b[k][j]) % P;
memcpy(c, t, sizeof t);
}
int qpow(int k)
{
int f0[N][N] = {1};
while (k)
{
if (k & 1) mul(f0, f0, a);
mul(a, a, a);
k >>= 1;
}
int res = 0;
for (int i = 0; i < m; i ++ )
res = (res + f0[0][i]) % P;
return res;
}
int main()
{
cin >> n >> m >> P >> str + 1;
for (int i = 2, j = 0; i <= m; i ++ )
{
while (j && str[i] != str[j + 1]) j = ne[j];
if (str[i] == str[j + 1]) j ++ ;
ne[i] = j;
}
for (int j = 0; j < m; j ++ )
for (int c = '0'; c <= '9'; c ++ )
{
int k = j;
while (k && c != str[k + 1]) k = ne[k];
if (c == str[k + 1]) k ++ ;
if (k < m) a[j][k] ++ ;
}
cout << qpow(n) << endl;
return 0;
}

本文作者:tmjyh09

本文链接:https://www.cnblogs.com/tmjyh09/p/17062986.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   tmjyh09  阅读(20)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起