思路(有点难理解)
这道题我觉得不是困难有点过分了…
结合了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;
}