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;
}