##[$AcWing$ $1052$. 设计密码](https://www.acwing.com/problem/content/1054/) ### 一、题目描述 你现在需要设计一个密码 $S$,$S$ 需要满足: - $S$ 的长度是 $N$ - $S$ 只包含小写英文字母($a$~$z$) - $S$ 不包含子串 $T$ 例如:$abc$ 和 $abcde$ 是 $abcde$ 的子串,$abd$ 不是 $abcde$ 的子串。 **请问共有多少种不同的密码满足要求**? 由于答案会非常大,请输出答案模 $10^9+7$ 的余数。 **输入格式** 第一行输入整数$N$,表示密码的长度。 第二行输入字符串$T$,$T$中只包含小写字母。 **输出格式** 输出一个正整数,表示总方案数模 $10^9+7$ 后的结果。 **数据范围** $1≤N≤50,1≤|T|≤N,|T|$是$T$的长度。 **输入样例1**: ```cpp {.line-numbers} 2 a ``` **输出样例1**: ```cpp {.line-numbers} 625 ``` **输入样例2**: ```cpp {.line-numbers} 4 cbc ``` **输出样例2**: ```cpp {.line-numbers} 456924 ``` ### 二、题目分析 ① $len=strlen(p+1)$ ,即$len$是模式串的长度 ```cpp {.line-numbers} scanf("%s", a + 1); //输入abcdef,共6个字符,放过下标为0的位置,从下标为1开始 int len = strlen(a + 1); //含义:从a下标偏移为1开始,计算到末尾\0的长度 printf("%d", len); //输出答案:6,理解:让从1开始,到末尾,尾巴在哪里自己找,计算返回长度 ``` ② 模式串是固定的,但$s$串是动态随便生成的,$s$串中的每个位置上都有$a \sim z$共$26$种可能 #### 闫氏$DP$分析法 **预求** 所有长度为$n$的生面的密码字符串中,不出现子串 $p$ 的方案数 **状态表示** - **集合** $f[i][j]$:密码已生成$i$位,并且,第$i$位匹配到子串$p$的位置是$j$的所有方案 - **属性** $count$(方案数) **状态转移** $\large f[i][j]$:已经成功构建了一个长度为$i$的密码,当前密码串与模式串的匹配位置是$j$的情况 思考它的下一步变化: - ① 下一步枚举尝试的字符,与$p$串的$p[j+1]$相等,并且,需要满足$p$串的$j+1 using namespace std; const int N = 55; const int MOD = 1e9 + 7; int n, ne[N]; int f[N][N]; char p[N]; int res; int main() { scanf("%d %s", &n, p + 1); // 读入模式串,存入到p数组中,下标从1开始 int m = strlen(p + 1); // 模式串的长度,读到\0结束,会自动计算出p串的长度 // kmp利用模式串求ne数组【模板】 for (int i = 2, j = 0; i <= m; i++) { while (j && p[i] != p[j + 1]) j = ne[j]; if (p[i] == p[j + 1]) j++; ne[i] = j; } f[0][0] = 1; // 递推起点,文本串0个字符,模板串0个字符,此时,方案数是1,其它状态都是0种方案 // 开始填充dp表,为什么i,j都要从0开始呢?这其实要先看一下状态转移方程f[i][j]出现在了方程中,而初始值是边界 // f[0][0]=1,所以填表的时候,自然也就是从i=0,j=0开始才能使用上初始值 for (int i = 0; i < n; i++) // 阶段 for (int j = 0; j < m && j <= i; j++) // 匹配长度,需要注意的是完成一个字符ch的匹配,则j++,所以,j的上限是m-1,以达到f[n][m]的最终状态 for (char ch = 'a'; ch <= 'z'; ch++) { // 准备填充的下一个字符ch int u = j; // 要退到哪里去呢? while (u && ch != p[u + 1]) u = ne[u]; // 不断回退,找到新起点 if (ch == p[u + 1]) u++; // 如果匹配下一个字符 f[i + 1][u] = (f[i + 1][u] + f[i][j]) % MOD; } // 枚举源串长度是n, 模式串匹配度不足m的,累加 for (int i = 0; i < m; i++) res = (res + f[n][i]) % MOD; cout << res << endl; return 0; } ```