【状态机DP】设计密码

题目描述

点我进入ACwing官方提交点

你现在需要设计一个密码 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

题目思路

  • 寻找最优子结构

S 的长度是 N;
S 只包含小写英文字母;
S 不包含子串 T;

  • 题目分析

题目的限制条件中说不能包含相同的子串,那么我们就会想到KMP,因为KMP解决的就是这类问题。
求数量的话,我们可能会想到数学方法或者是DP方法,两种方法依次尝试,看哪一种更合适一点。

尝试之后发现,就是要使用KMP+DP做这个题目。

  • DP集合分析

因为会输入长度,那么长度这个变量是要加入集合表示中的。
接下来考虑它是如何进行转移的,KMP的写法中,它的匹配是一个跳一个的,那么我就依据这个来分析他的状态转移方程。
一个状态转移为另一个状态,这不就是状态机吗。

使用状态机的DP思路思考。
f [ i , j ] f[i, j] f[i,j]
集合表示:有长度为i,最后一个字符状态为j的符合条件的字符串数量。
属性:数量。

状态划分就是以j的下一个状态的点为分界点就行组合。
在每一个位置枚举26个字母,也就是将所有情况都枚举了一遍,我们只需要KMP数组中的长度没有达到序列的总长度即表示这种情况没有包含子串T。

  • 初始化

在长度为0,状态为0的时候,是算作一种情况。

AC 代码 O ( N 3 ) O(N^3) ON3

#include<bits/stdc++.h>
using namespace std;

#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define For(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(a) cout << #a << " = " << a << ENDL
#define mod(x) (x) % MOD
#define ENDL "\n"
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;

const int N = 50 + 5, MOD = 1e9 + 7;
int f[N][N], ne[N];
char T[N];

int main() {
#ifdef LOCAL
	freopen("data.in", "r", stdin);
#endif // LOCAL
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int n;
	cin >> n >> T + 1;
	int 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(i, 0, n) _for(j, 0, m) for (char k = 'a'; k != 'z' + 1; ++k) {
		int u = j;
		while (u && k != T[u + 1]) u = ne[u];
		if (T[u + 1] == k) ++u;
		if (u < m) f[i + 1][u] = mod(f[i + 1][u] + f[i][j]);
	}

	int ans = 0;
	_for(i, 0, m) ans = mod(ans + f[n][i]);
	cout << ans << ENDL;
	return 0;
}

AC 代码 O ( N 2 ) 预 处 理 O(N^2)预处理 ON2

#include<bits/stdc++.h>
using namespace std;

#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define For(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(a) cout << #a << " = " << a << ENDL
#define mod(x) (x) % MOD
#define ENDL "\n"
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;

const int N = 50 + 5, MOD = 1e9 + 7;
int f[N][N], ne[N], last[N][26];
char T[N];

int main() {
#ifdef LOCAL
	freopen("data.in", "r", stdin);
#endif // LOCAL
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int n;
	cin >> n >> T + 1;
	int 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;
	}

	_for(j, 0, m) for (char k = 'a'; k != 'z' + 1; ++k) {
		int u = j;
		while (u && k != T[u + 1]) u = ne[u];
		if (T[u + 1] == k) ++u;
		last[j][k - 'a'] = u;
	}

	f[0][0] = 1;
	_for(i, 0, n) _for(j, 0, m) for (char k = 'a'; k != 'z' + 1; ++k) {
		int u = last[j][k - 'a'];
		if (u < m) f[i + 1][u] = mod(f[i + 1][u] + f[i][j]);
	}

	int ans = 0;
	_for(i, 0, m) ans = mod(ans + f[n][i]);
	cout << ans << ENDL;
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值