码迷,mamicode.com
首页 > 其他好文 > 详细

UVA1401 【Remember the Word】

时间:2021-06-19 18:45:40      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:ber   知识点   模式   rem   else   span   说明   main   题解   

知识点:dp+trie

这道题显然是从前往后dp的,题解区里还没有这样的题解,我就来发一波(题解区里也有提到不过并没有做详细的说明也没有代码)。

思路和从后往前的基本一样。

\(dp_i\)代表前缀 \(s_{1...i}\) 有多少种不同的组成方式。

很容易想到转移方程:

如果一个模式串 \(t\) 是前缀 \(s_{1...i}\) 的后缀,则 \(dp_i += dp_{i-|t|}\)

如果暴力去判断的话复杂度是 \(O(nm)\) 的,这道题过不去,所以我们要考虑优化。

我们发现每个模式串最长也才 \(100\),考虑将所有模式串倒序建成一个 \(trie\)

对于 \(s\) 的一个前缀,将其倒叙放到 \(trie\) 上匹配,如果经过结束节点就说明匹配到了一个模式串,\(dp\) 值加上其即可。

因为模式串最长 \(100\),即 \(trie\) 树的深度最大才 \(100\),所以复杂度是\(O(100n)\)的。

多组数据注意初始化。

#include <bits/stdc++.h>
using namespace std;
int trie[400010][26], tot;
char s[300010], t[110];
int n, m;
int End[400010], dp[300010];
int cnt;
int main() {
	while (scanf("%s", s + 1) != EOF) {
		tot = 1;
		memset(End, 0, sizeof(End));
		memset(trie, 0, sizeof(trie));
		cnt++;
		n = strlen(s + 1);
		scanf("%d", &m);
		for (int i = 1, len, p; i <= m; i++) {
			scanf("%s", t + 1);
			len = strlen(t + 1);
			p = 1;
			for (int j = len; j; j--) {
				if (!trie[p][t[j] - ‘a‘]) trie[p][t[j] - ‘a‘] = ++tot;
				p = trie[p][t[j] - ‘a‘];
			}
			End[p]++;
		}
		memset(dp, 0, sizeof(dp));
		dp[0] = 1;
		for (int i = 1, p; i <= n; i++) {
			p = 1;
			for (int j = i; j; j--) {
				if (trie[p][s[j] - ‘a‘]) p = trie[p][s[j] - ‘a‘];
				else break;
				if (End[p]) {
					dp[i] += (dp[j - 1] * End[p]) % 20071027;
					dp[i] %= 20071027;
				}
			}
		}
		printf("Case %d: %d\n", cnt, dp[n]);
	}
	return 0;
}

UVA1401 【Remember the Word】

标签:ber   知识点   模式   rem   else   span   说明   main   题解   

原文地址:https://www.cnblogs.com/zcr-blog/p/14901189.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!