设计密码 —— 状态机

来源:acwing

题目描述

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

·S 的长度是 N;
·S 只包含小写英文字母;
·S 不包含子串 T;
例如:abc 和 abcde 是 abcde 的子串,abd 不是 abcde 的子串。

请问共有多少种不同的密码满足要求?

由于答案会非常大,请输出答案模 109+7 的余数。
三重循环,时间复杂度为O(nm)

输入描述

第一行输入整数N,表示密码的长度。

第二行输入字符串T,T中只包含小写字母。

输出描述

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

数据范围

1≤N≤50 ,
1≤|T|≤N,|T|是T的长度。

分析

这道题当初思考了很久,因为状态转移方程那个地方被卡住了

这道题结合了kmp算法和状态机模型就比较有意思,题目要求密码中不能出现给定的字符串,也就是说,我可以包含一部分子串,甚至不包含子串,只要不会有一段跟给定的字符串完全匹配上就好了,提到字符串匹配问题,这里就要涉及到KMP算法

所以,这道题我们可以写两重循环,枚举前i个字符,假定这i个字符里面有j个字符跟给定的字符串对上了,然后枚举第i + 1个字符的26种可能情况,然后跑一下KMP,判断会有多少位字符匹配成功,如果完全匹配就continue,否则就进行状态转移

代码

#include <iostream>
#include <cstring>

using namespace std;

const int N = 55,mod = 1e9 + 7;;
int f[N][N];
char str[N];
int n,m;
int ne[N];

int main(){
    cin >> n >> str + 1;
    m = strlen(str + 1);
    f[0][0] = 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;
    }
    for(int i = 0;i < n;i++){
        for(int j = 0;j < m;j++){
            for(char k = 'a';k <= 'z';k++){
                int u = j;
                while(u && k != str[u + 1]) u = ne[u];
                if(k == str[u + 1]) u++;
                if(u < m) f[i + 1][u] = (f[i + 1][u] + f[i][j]) % mod;
            }
        }
    }
    int ans = 0;
    for(int i = 0;i < m;i++) ans = (ans + f[n][i]) % mod;
    cout << ans << endl;
}

猜你喜欢

转载自blog.csdn.net/tlyzxc/article/details/107829665