KMP+状态机

1052. 设计密码

你现在需要设计一个密码 S,S 需要满足:
S 的长度是 N;
S 只包含小写英文字母;
S 不包含子串 T;
例如:abc 和 abcde 是 abcde 的子串,abd 不是 abcde 的子串。
请问共有多少种不同的密码满足要求?
由于答案会非常大,请输出答案模 109+7 的余数。
输入格式
第一行输入整数N,表示密码的长度。
第二行输入字符串T,T中只包含小写字母。
输出格式
输出一个正整数,表示总方案数模 109+7 后的结果。
数据范围
(1≤N≤50, 1≤|T|≤N,|T|)是T的长度。
输入样例1:
2
a
输出样例1:
625
输入样例2:
4
cbc
输出样例2:
456924

思路:
如果不考虑条件3,答案就是26^n,每个点之间的状态互不影响。由于考虑了条件3,当前假设T串是‘abc’,S串的第i位的状态是'c',那么就不能由第i-2的'a'走到i-1的'b'再走到这个状态。首先当前枚举当前位置的字母预处理模式串已经匹配了j位时下一步会匹配到模式串的哪一位,注意如果匹配到了最后一位需要赋值为-1,表示不可达。
首先枚举文本串的位置,然后枚举模式串的位置,最后时文本串当前位置的字母,利用kmp预处理的数组跳到i+1的应该跳到的状态。这道题由i跳到i+1,所以i+1的答案在枚举i时统计。

#include<bits/stdc++.h>
using namespace std;
const int N=60,mod=1e9+7;
int f[N][N];
int nex[N],nextc[N][N];
char str[N];
int main(){
    int n;
    cin>>n;
    cin>>(str+1);
    int m=strlen(str+1);
    for(int i=2,j=0;i<=n;++i){
        while(j&&str[j+1]!=str[i]) j=nex[j];
        if(str[j+1]==str[i]) ++j;
        nex[i]=j;
    }
    for(char i='a';i<='z';++i){
        for(int j=0;j<m;++j){
            int u=j;
            while(u&&str[u+1]!=i) u=nex[u];
            if(str[u+1]==i) ++u;
            if(u==m) nextc[i-'a'][j]=-1;
            else nextc[i-'a'][j]=u;
        }
    }
    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){
                int u=nextc[k-'a'][j];
                if(u==-1) continue;
                else {
                    f[i+1][u]+=f[i][j];
                    f[i+1][u]%=mod;
                }
            }
        }
    }
    int res=0;
    for(int i=0;i<m;++i){
        res+=f[n][i];
        res%=mod;
    }
    cout<<res;
    return 0;
}
原文地址:https://www.cnblogs.com/jjl0229/p/12625300.html