思路(有点难理解)
这道题我觉得不是困难有点过分了......
结合了KMP和复杂状态机,属实难
好了,不抱怨了,接下来我讲讲我是怎么理解y总的思路的
首先怎么确定状态,这里把 f[i][j] 表示为对于现在生成的密码已经到了第 i 个了,并且当前在子串中的位置 是 j 的密码个数。(不懂KMP的小伙伴建议去看y总视频,我是看了1个小时才看懂的,你们应该比我强)、
一个状态机问题,先要明确有几种状态,对于每一个固定的i 和 j 来说,总共有26种状态,对于26个字母。
如何判断某一种方案是可能的呢?联系KMP的子串匹配方法,就是判断对于固定的 i 和 j 判断当前字符是不是和子串中 j+1 的字符匹配,匹配就j++,不匹配 j 就回跳,如果我们到最后,也就是 i = 26 的时候都没办法使 j 到子串的末尾,那么就意味着我们的密码中没有子串,也就是一种可能。
根据上面的分析,我们再来看这个状态的定义,想一想状态方程,因为每一个字母都对于固定的 i 和 j 都有固定的判断结果,那么我们只要对于每一种 i 和 j枚举一下26个字母 ,根据上面的判断方法判断一下是否可能 如果可能就可以状态转移了
接下来,是我写题解的惯例,写一下代码每一步的含义和其中一些细节
1:KMP的预处理,初始化next数组,代码中用ne表示
2:循环:
2.1:第一层循环:枚举一下i的位置,也就是当前密码的长度(代码中是从0开始的)
2.2:第二层循环:枚举一下j 的位置,也就是再子串中的位置
2.3:第三层循环:枚举一下 所有的字母 ,并且应KMP判断是否当前密码有子串,如果没有更新f[i+1][j],为什么是i+1,而不是 i 呢?因为这里定义的状态是已经有的长度,不包括当前枚举的字母,我当时也迷惑了一会,现在写出来帮助一下不懂的小伙伴
3:最后把所有可能的 j 的位置加起来,就是答案,因为i最后肯定是 n ,所以枚举一下未知的 j 。
KMP中还有一个细节,就是要用u来对每一种状态更新,不要用j,不要搞错了,j是枚举的状态,如果用 j 的话更新的状态就不对了,注意一下哈。
代码
#include <cstring>
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
const int N=55,mod=1e9+7;;
int ne[N];//KMP中的next数组
char str[N];//子串
int f[N][N];//表示对于枚举到的那个点的某个状态的解
int main(){
int n,m;
cin >> n >> str+1;
m=strlen(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;
}
f[0][0]=1;
for(int i=0;i<n;i++){//枚举每一个点
for(int j=0;j<m;j++){//枚举字串中的位置
for(char k='a';k<='z';k++){//枚举i处的字符
int u=j;
while(u&&k!=str[u+1])u=ne[u];
if(str[u+1]==k)u++;
if(u<m){//说明没有出现过子串
f[i+1][u]=(f[i+1][u]+f[i][j])%mod;//这里最后的状态的f[i+1][u],所以更新的是这个值
}
}
}
}
int res=0;
for(int i=0;i<m;i++)res = (res + f[n][i]) % mod;//把所有的情况加起来
cout << res;
}
tql
那个枚举的k是i+1处的字符还是i处的字符?
应该是i + 1
通过j求出跳转后的状态u后,u的值表达的是模式串的1~u位正好匹配母串的最后u位,转移方程求得是f[i + 1][u],即i + 1位匹配了模式串第u位,也就是k
KMP我看了一个下午才看懂
大佬tql
楼主太棒了
为啥要初始化f[0][0] = 1有点不明白
因为在枚举第一个点并且状态为0的时候只有一种解,这是所有f[i][j]的初始点,后面的f[i][j]的值都是通过这个推到过来的,所以一定要初始化
请问f[i+1][u]=(f[i+1][u]+f[i][j])%mod这是在更新 什么 。属实没看懂,脑子里整个过程都比较乱。这题跟状态机怎么结合没太懂
这个是在密码已经构造到第i个字母,并且子串匹配状态为j的时候,如果对于取k这个字母时,没有匹配到子串,也就是密码合法的时候,子串位置 j 会跳回到u这个位置,所以f[i+1][u]+=f[i][j],建议多理解一下KMP,和f[i][j的定义,(ง •_•)ง
我看一天了, 爷不看了
集美集美, 类似于背包问题里面求方案数一样, 这里f[n][m]是指主串第n个位置, 字串匹配到第m个位置的方案数;
这里i, j 是从0开始的, 所以在更新的时候是f[i+1][u] = f[i+1][u] + f[i][j], 这里的u是已经匹配到最新的子串的位置;
按以前的写法应该是f[i][u] = f[i][u] + f[i-1][j-1];(i, j 从1开始的话) 我看了一天
啊,这要理解kmp的匹配原理,我一直感觉是有重复的(看了一天还不懂- .-)
我不太理解这里为什么要+=,f[i + 1][u] 有可能有很多次被更新的可能吗?