1052. 设计密码

思路(有点难理解)

这道题我觉得不是困难有点过分了…
结合了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;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值