C++---状态机模型---设计密码 (每日一道算法2023.4.14)

注意事项:
本题为"KMP字符串匹配"的扩展题,建议先阅读这篇文章并理解。
状态机的基本定义:状态机-百度百科

题目:
你现在需要设计一个密码 S,S 需要满足:

  • S 的长度是 N;
  • S 只包含小写英文字母;
  • S 不包含子串 T;

例如:abc 和 abcde 是 abcde 的子串,abd 不是 abcde 的子串。
请问共有多少种不同的密码满足要求?
由于答案会非常大,请输出答案模 10e9+7 的余数。

输入格式
第一行输入整数N,表示密码的长度。
第二行输入字符串T,T中只包含小写字母。

输出格式
输出一个正整数,表示总方案数模 10e9+7 后的结果。

数据范围
1≤N≤50,
1≤|T|≤N,|T|是T的长度。

输入:
2
a
输出:
625
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 55, mod = 1000000007;
int n, m;  //密码长度n,子串长度m,
char s[N]; //子串,
int f[N][N], ne[N];     

int main() {
    //读入
    cin >> n >> s+1;
    m = strlen(s+1);

    //kmp的next数组处理,
    for (int i = 2, j = 0; i<=n; i++) {
        while (j != 0 && s[i] != s[j+1]) j = ne[j];
        if (s[i] == s[j+1]) j++;
        ne[i] = j;
    }

    f[0][0] = 1;  //初始化,对于第0位密码,且匹配字串的位置为0时的方案数为1,
    
    for (int i = 0; i<n; i++) {                 //枚举密码长度,
        for (int j = 0; j<m; j++) {             //枚举密码长度为i,且子串s匹配的位置为j时的状态,
            for (char k = 'a'; k<='z'; k++) {   //对于第i+1位字母,枚举26种可能的情况,
                //当前密码位数为i,且子串匹配的位置是j,尝试用i+1为k的情况进行跳转,
                //1.如果u能够跳转到子串s的最后一位(u==m),说明密码第i+1位为k时,密码中会包含子串s,
                //2.反之则说明当密码的i+1位为k时不会包含子串s (u<m),也就是合法方案,可以进行更新,
                int u = j;
                while (u != 0 && k != s[u+1]) u = ne[u];
                if (k == s[u+1]) u++;
                if (u < m) {
                    f[i+1][u] = (f[i+1][u] + f[i][j]) % mod;
                }
            }
        }          
    }

    //求方案总和,这里不取到m是因为当匹配到子串的第m位时,说明当前方案包含了整个子串,显然是不合法的方案,
    int res = 0;
    for (int i = 0; i<m; i++) res = (res + f[n][i]) % mod;  
    cout << res;
    return 0;
}

思路:
题目中要求有26个小写字母,那么对于密码中的第i位来说,就有26种不同的选择,而对于每种选择,又都有m(子串长度)种可跳转到的状态,
(kmp next数组向前跳转 或 当前字母相等就向后跳转一位)。

而且对于不同的子串,跳转状态也不同,比起前两天的点对点的状态机来说,这题明显要复杂的多。

还是经典的y式dp分析法,
1.状态表示
f[i][j]:
对于第i位密码,且子串匹配(KMP匹配)的位置为j时的所有方案,属性为Sum。

2.状态计算
从代码的实际含义出发,当密码的i+1位为k时不包含子串s (u<m),也就是合法方案时,可以进行更新, 直接从f[i][j] (有i位密码时且子串匹配为第j位)转移过来:
f[i+1][u] += f[i][j]

如果有所帮助请给个免费的赞吧~有人看才是支撑我写下去的动力!

声明:
算法思路来源为y总,详细请见https://www.acwing.com/
本文仅用作学习记录和交流

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值