状态机dp 你现在需要设计一个密码需要满足; 的长度是 只包含小写英文字母 不包含子串

例如: 的子串, 不是 的子串。 请问共有多少种不同的密码满足要求? 由于答案会非常大,请输出答案模的余数。

数据范围

, 的长度 只包含小写英文字母

输入格式

输出格式

一个数,表示总方案数模

样例1

输入:

2
a

输出:

625
样例2

输入:

4
cbc

输出:

456924
样例3

输入:

3
xy

输出:

17524
样例4

输入:

50
password

输出:

448890425

思路

简单看一下案例1, 显然所有不包含子串a的长度为2的S, 其数量就是 25*25 = 625(去掉a后就剩下25个字母能选)。

边构造边判断是否为字串, 判断子串则可以使用KMP算法。

KMP算法过程是: 先构建模板n

for(int i = 1, j = 0; i <= m; i++)
{
	while(j && s[i] != s[j + 1]) j = ne[j]; 
	if(s[i] == s[j + 1]) j++;
	ne[i] = j;
}

接着对于目标字符串q

for(int i = 0, j = -1 ; i <= n; i++)
{
	while(j != -1 && q[i] != s[j + 1]) j = ne[j];
	if(q[i] == s[j +1]) j++;
	if(m == j) cnt++; // 子串数加一
}

如何得到所有长度为n的S串呢?普通循环是不行的, 这里需要使用DFS深搜来枚举。 其时间复杂度为 。显然是不行的。 既然需要从所有状态中得到可行状态, 那有没有方法可以直接根据可行状态开始计算呢?从一个可行状态转移到另一个可行状态, 直到得到所有可行状态。

需要从T串入手: 可行状态肯定满足KMP匹配的模板串长度小于m, 也就是说所有长度小于m的情况都是可行状态。

这些状态之间有什么关系呢?

KMP的匹配过程其实就是一个状态自动机, 在j位置上如果不能调到j+1, 则调到 ne[j]。即长度为j时可以转移到长度为j+1或长度为ne[j]。 设 为可行状态的长度为j时的方案 可得: 在结尾时将所有长度情况的可行状态的方案数加起来, 就是总方案数。 而对于每个i, 都有26种对应的可行状态, 因此状态定义为: f[i][j]: 已经确定i长度, 且其匹配的s串位置为j, 要枚举i+1的字符。 此时所谓的确定的i长度p串, 匹配的j长度s串都是抽象的, 但一定存在。

初始状态f[0][0] = 1 最终答案sum(f[n][i]) i = (0,1,2,...,m-1)

状态更新: 令 u = j, 维持状态恒定 当p[i + 1] != s[u + 1]时, 执行u = ne[u], 直到相等为止 相等后将u++ 此时说明f[i + 1][u] 可以从 f[i][j] 也就是当前状态 转移而来。 则将f[i + 1][u] += f[i][j]

该题的DP模式是刷表法, 即用一个状态去更新以后的状态, 而不是根据之前确定的状态来设定当前的状态(填表法)。

#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <vector>
using namespace std;
//------- Coding Area ---------//
const int N = 55, mod = 1e9 + 7;
int n, m;
char T[N];
int ne[N];
long long ans = 0;
int f[N][N];
 
int main()
{
    cin >> n >> T + 1;
    m = strlen(T + 1);
    for (int i = 2, j = 0; i <= m; i++)
    {
        while (j && T[i] != T[j + 1])
            j = ne[j];
        if (T[i] == T[j + 1])
            j++;
        ne[i] = j;
    }
 
    f[0][0] = 1;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < i && j < m; j++)
        {
            for (char c = 'a'; c <= 'z'; c++)
            {
                int u = j;
                while (u && T[u + 1] != c)
                    u = ne[u];
                if (T[u + 1] == c)
                    u++;
                if (u < m)
                    f[i + 1][u] = (f[i + 1][u] + f[i][j]) % mod;
            }
        }
    }
    for (int i = 0; i < m; i++)
        ans = (ans + f[n][i]) % mod;
 
    cout << ans << endl;
    return 0;
}