1. 程式人生 > 其它 >UVA1401 【Remember the Word】

UVA1401 【Remember the Word】

知識點: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;
}