传送门

这道题目的题意描述,通俗一点说就是这样:有一个长度为n的数字串(其中每一位都可以是0到9之间任意一个数字),给定一个长度为m的模式串,求有多少种情况,使得此模式串不为数字串的任意一个子串。结果对给定的模数取模。

我们为了阅读方便,将数字串称为P串,给定的模式串称为T串。

一开始有这么个暴力想法,就是直接把T串往P串里面匹配,算出有多少种不合法的情况再计算,不过这样并不行,因为在这种算法中有很多种不合法情况被重复计算了。

于是乎看了题解(看题解也看不懂的我)。我们使用dp[i][j]表示在P串中枚举到第i位时,P串有长为j的后缀与T串中长为j的前缀相匹配的情况数。

注意指的是P串枚举位置之前。这样的话,答案就是dp[n][0] + dp[n][1] + …… dp[n][m-1].

也许有疑问,为什么一定要把枚举到P串末尾的情况才算作答案呢?如果在中间随意匹配几个数不可以吗?亦或者,如果中间有不合法情况呢?

我们注意一下转移方式,肯定dp[i][j]是从dp[i-1][j]转移过来的。转移过来的状态肯定是合法的,所以也就保证了枚举到P串第i位时,所有的情况都能保证P串前面没有不合法情况,即完全与T串匹配的情况。所以前面第一种所说的情况被包含,第二种保证不会存在。(具体可以看下面)

既然如此,那我们说说怎么转移,上面说过,枚举到第i位时的情况必然是由枚举到第i-1位时的情况转移过来的。而对于正在被枚举的第i位,可以填入0~9之中任意一个数字,填入之后对于当前匹配的前后缀长度(就是j)有以下三种影响:

1.匹配长度变为0

2.匹配长度在原来的基础上+1

3.匹配长度变为一个在0~原匹配长度之间的数

我们看到这里突然发现,这里和KMP的过程十分相似。KMP就是在逐位枚举的过程中不断确定在这一位字符之前的最长可匹配的前后缀长度,这里也一样。

所以我们只需要先求一遍T串的next数组,之后在DP的时候使用next计算转移方式即可。这里注意一下,虽然分为三种情况,不过实际上写代码的时候直接写一种处理就可以,因为第1,3中情况是失配时候递归调用next的,而第二种情况是匹配上的,直接写在一起就可以啦。

讲了这么多还没说转移方程……通过上面的思路可以知道,转移方程为:dp[i][j] = dp[i-1][0] * a[o][j] + dp[i-1][1] * a[1][j] + …… + dp[i-1][m-1] * a[m-1][j];

其中a[i][j]表示当前匹配长度由i变为j有多少种方法,这个是固定的数目,可以预处理出来。具体的预处理方法就是按照上面的方法,枚举当前匹配长度,从0~m-1,再枚举当前的数是多少(0~9),比如你现在枚举当前匹配长度为i,如果下一位与T串中对应位置匹配,那么a[i][i+1]++,否则令i = next[i],重复上述过程即可。(很像KMP)这样就顺便可以解释为什么没有不合法情况,因为,我们只枚举匹配长度在0~m-1之间的情况,之后的情况我们不枚举,这样自然不会有不合法情况产生。

可能有人会觉得奇怪,比如你从长度为2不可能直接变到长度为5,6……,那么这些情况自然就是0了,还有就是有人会疑问每一位的数字可能不同,怎么方法数是固定的,因为这是你已经枚举的,枚举的时候是会自动考虑所有情况的,可能有点难理解,不过想一想意会一下还是可以的。

DP方程已经好了,不过还没完,n的范围到了10^9,这样O(n)是承受不起的。我们需要使用某些手段加速。重新观察一下DP方程,既然a数组是一个固定的值……

那就可以使用矩阵乘法优化。(因为每一步,dp[i][0~m-1]都是由dp[i-1][0~m-1]通过固定方式转化过来的,想想Fibonacci!)

我们偷一下某位大神的图来说明这件事。

这样就很一目了然了。调用一下矩阵快速幂,直接求a的n次幂,并与一开始构造的单位矩阵相乘即可。(一开始很明显只有f[i][i]是1,其余都是0)

(唉我真是学啥忘啥,这KMP和矩阵乘法都快不会了)

看一下代码。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')
using namespace std;
const int maxn = ;
int n,mod,len,nxt[maxn],res;
char str[maxn];
struct matrix
{
int f[maxn][maxn];
matrix()
{
memset(f,,sizeof(f));
}
friend matrix operator * (const matrix &a,const matrix &b)
{
matrix c;
rep(i,,len-)
rep(j,,len-)
rep(k,,len-)
c.f[i][j] += a.f[i][k] * b.f[k][j],c.f[i][j] %= mod;
return c;
}
}m,ans; void getnext()
{
int j = ;
nxt[] = ;
rep(i,,len)
{
while(j && str[j+] != str[i]) j = nxt[j];//这种求next是从0开始的,从-1也可以
if(str[j+] == str[i]) j++;
nxt[i] = j;
}
// rep(i,1,len) printf("%d ",nxt[i]);
}
void dp()
{
rep(i,,len-)
rep(j,,)
{
int k;
for(k = i; k; k = nxt[k]) if(str[k+] - '' == j) break;
if(str[k+] - '' == j) k++;
m.f[i][k]++,m.f[i][k] %= mod;
}
return;
}
void pow()
{
rep(i,,len-) ans.f[i][i] = ;
//ans是构造的单位矩阵
while(n)
{
if(n&) ans = ans * m;
m = m * m;
n >>= ;
}
return;
}
int read()
{
int ans = ,op = ;
char ch = getchar();
while(ch < '' || ch > '')
{
if(ch == '-') op = -;
ch = getchar();
}
while(ch >='' && ch <= '')
{
ans *= ;
ans += ch - '';
ch = getchar();
}
return ans * op;
}
int main()
{
n = read(),len = read(),mod = read();
scanf("%s",str+);
getnext(),dp(),pow();
rep(i,,len-) res = (res + ans.f[][i]) % mod;
printf("%d\n",res);
return ;
}
/*
4 3 100
111
*/

HNOI2008 GT考试 (KMP + 矩阵乘法)的更多相关文章

  1. [bzoj1009][HNOI2008]GT考试——KMP+矩阵乘法

    Brief Description 给定一个长度为m的禁止字符串,求出长度为n的字符串的个数,满足: 这个字符串的任何一个字串都不等于给定字符串. 本题是POJ3691的弱化版本. Algorithm ...

  2. BZOJ 1009 [HNOI2008]GT考试 (KMP+矩阵乘法)

    ---恢复内容开始--- 题目大意:给定一个由数字构成的字符串A(len<=20),让你选择一个长度为n(n是给定的)字符串X,一个合法的字符串X被定义为,字符串X中不存在任何一段子串与A完全相 ...

  3. BZOJ1009: [HNOI2008]GT考试(KMP+矩阵乘法)

    Description 阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字.他的不吉利数学A1A2...Am(0< ...

  4. BZOJ_1009_[HNOI2008]GT考试_KMP+矩阵乘法

    BZOJ_1009_[HNOI2008]GT考试_KMP+矩阵乘法 Description 阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考 ...

  5. BZOJ 1009 [HNOI2008]GT考试 (KMP + 矩阵快速幂)

    1009: [HNOI2008]GT考试 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 4266  Solved: 2616[Submit][Statu ...

  6. bzoj 1009: [HNOI2008]GT考试 -- KMP+矩阵

    1009: [HNOI2008]GT考试 Time Limit: 1 Sec  Memory Limit: 162 MB Description 阿申准备报名参加GT考试,准考证号为N位数X1X2.. ...

  7. [bzoj1009](HNOI2008)GT考试 (kmp+矩阵快速幂加速递推)

    Description 阿 申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字.他的不吉利数学 A1A2...Am(0&l ...

  8. 题解:BZOJ 1009 HNOI2008 GT考试 KMP + 矩阵

    原题描述: 阿申准备报名参加GT考试,准考证号为N位数 X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字.他的不吉利数学A1A2...Am(0<=Ai&a ...

  9. bzoj 1009 [HNOI2008]GT考试——kmp+矩阵优化dp

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1009 首先想到 确保模式串不出现 就是 确保每个位置的后缀不是该模式串. 为了dp,需要记录 ...

  10. bzoj1009 [HNOI2008]GT考试——KMP+矩阵快速幂优化DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1009 字符串计数DP问题啊...连题解都看了好多好久才明白,别提自己想出来的蒟蒻我... 首 ...

随机推荐

  1. 顿悟:Linux是拿来用的,不是拿来折腾的

    Linux是拿来用的,而不是折腾其本身.相信这个道理不少聪明人(实用主义者)都明白,然而总是有那么一群人拿Linux去安装各种发行版.研究Linux命令.配置桌面.美化桌面.研究各种wm/DE.永无止 ...

  2. Python 和 Elasticsearch 构建简易搜索

    Python 和 Elasticsearch 构建简易搜索 作者:白宁超 2019年5月24日17:22:41 导读:件开发最大的麻烦事之一就是环境配置,操作系统设置,各种库和组件的安装.只有它们都正 ...

  3. STL优先队列模板

    1. 优先队列 用途:按照某一个关键字对插入元素或删除元素后的数据集进行自动排序 复杂度: logN 2. 数据声明 (1)头文件:#include<queue> (2)声明:  prio ...

  4. windows安装RabbitMQ注意事项

    1.首先下载好ERLANG.RabbitMQ安装包,先安装erlang,设置好环境变量,然后再去安装MQ; 2.别人有两个报错: 一:RabbitMQ安装目录中不允许有空格; 二:安装rabbitmq ...

  5. luogu P1080 国王游戏

    题目描述 恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排成一排,国王站在队伍的最 ...

  6. [Bzoj5043][Lydsy1709月赛]密码破译(按位dp)

    5043: [Lydsy1709月赛]密码破译 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 477  Solved: 125[Submit][Sta ...

  7. java基础之IO流(一)字节流

    java基础之IO流(一)之字节流 IO流体系太大,涉及到的各种流对象,我觉得很有必要总结一下. 那什么是IO流,IO代表Input.Output,而流就是原始数据源与目标媒介的数据传输的一种抽象.典 ...

  8. lua 暂停写法

    由于lua 不支持暂停 用其他方法变相实现 -- 暂停 hock 写法 function _M.sleep(n) if n > 0 then os.execute("ping -c & ...

  9. BZOJ 3363 POJ 1985 Cow Marathon 树的直径

    题目大意:给出一棵树.求两点间的最长距离. 思路:裸地树的直径.两次BFS,第一次随便找一个点宽搜.然后用上次宽搜时最远的点在宽搜.得到的最长距离就是树的直径. CODE: #include < ...

  10. viewcontroller生命周期知识要点

    一 viewcontroller执行方法的主要顺序为: init—>loadView—>viewDidLoad—>viewWillApper—>viewDidApper—> ...