/
核心思路: 用f[i][j] 表示 在确定第 i 个字母的填法时,匹配串,中,能够和原串匹配的 j 个位置的 种类数目
比如第二个例子,
4
cbc f[0][0] 表示填第 0 个字母,空串也是一种方法。
当第一个字母 ‘a’时 能由 f[0][0] 转移过来, + 1
但是不能由 f[0][1~3] 转移过来,因为,只有一个字母’a’ 不可能 已经匹配了1个或者三个
当填 第三个字母 f[3][1] 时 若字母为 ‘c’时 则在子串中最多 匹配 的 j 为 0 或者 2 可以由f[2][0]
转移过来, 当f[2][0] 即 在填第二个字母时 匹配了 0个 模式串的字母,那么,在填第三个字母时,
字母为’c’ 就能匹配一个,而其他字母 如 ‘a’ 只能匹配 0 个 1 个两个
‘c’ 可以匹配三个,但是不合法。
/
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 60; const int mod = 1e9 + 7;
char str[N];
int f[N][N];
//f[i][j] 表示 当前考虑的第i个字母, 且子串 中位置已经匹配了j个字母 的密码设计方法。
int ne[N];
int main()
{
int n, m; cin >> n >> (str + 1) ;
m = strlen(str + 1);
for(int i = 2, j = 0; i < m; i ++)
{
while(j && str[j + 1] != str[i]) j = ne[j];
if(str[j + 1] == str[i]) j ++;
ne[i] = j;
}
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 ++) //这里的字母是第i+1个位置出现的字母。
{
int u = j;
while(u && k != str[u + 1]) u = ne[u];
//如果 j == m - 1 且第i + 1个字母恰好为 模式串的末尾字符,那么,u变成m
//就是不合法的,那么就不会更新。 如果 不是末尾字符 就跳转到能够匹配的最大的j 假如 cbc 字母为'c'
//f[i + 1][m - 1] 就不会
if(k == str[u + 1]) u++;
if(u < m)
{
f[i + 1][u] = (f[i + 1][u] + f[i][j]) % mod;
}
}
}
int res = 0;
for(int i = 0; i < m; i ++) res = (res + f[n][i]) % mod;
cout << res << endl;
}