【LA3942】Remember the Word【Trie】【计数DP】

https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&category=22&page=show_problem&problem=1943

大白上的题。

设dp[i]表示后缀S[i...L]的分解方案数,那么有dp[i] = ∑(dp[i + len(x)),单词x是S[i...L]的前缀。

每次转移,去trie里搜索S[i...L],如果遇到单词节点,那么加上dp[i + len(x)]。


给数组清零,没发现数组大小不一样,RE一发...

/* Footprints In The Blood Soaked Snow */
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn = 300005, maxm = 4005, maxnode = 400005, p = 20071027;

int n, dp[maxn], son[maxnode][27], cnt;
bool flag[maxnode];
char str[maxn], s[maxm];

inline void insert(int len) {
	int now = 0;
	for(int i = 0; i < len; i++) {
		int &pos = son[now][s[i] - 'a'];
		if(!pos) pos = ++cnt;
		now = pos;
	}
	flag[now] = 1;
}

inline int calc(int x) {
	int now = 0, ans = 0;
	for(int i = x; i < n; i++) {
		int &pos = son[now][str[i] - 'a'];
		if(!pos) return ans;
		if(flag[pos]) ans = (ans + dp[i + 1]) % p;
		now = pos;
	}
	return ans;
} 

int main() {
	int cas = 1;
	while(scanf("%s", str) != EOF) {
		for(int i = 0; i < maxnode; i++) flag[i] = 0;
		memset(son, 0, sizeof(son)); cnt = 0;

		scanf("%d", &n);
		for(int i = 1; i <= n; i++) {
			scanf("%s", s);
			insert(strlen(s));
		}

		n = strlen(str);
		dp[n] = 1;
		for(int i = n - 1; i >= 0; i--) dp[i] = calc(i);

		printf("Case %d: %d\n", cas++, dp[0]);
	}
	return 0;
}


你可能感兴趣的:(trie)