Trie + DP LA 3942 Remember the Word

题目传送门

题意:(训练指南P209) 问长字符串S能由短单词组成的方案数有多少个

分析:书上的做法。递推法,从后往前,保存后缀S[i, len-1]的方案数,那么dp[i] = sum (dp[i+len(s)])。用字典树记录并查询短单词的前缀的长度。

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

const int L = 3e5 + 5;
const int N = 4e3 + 5;
const int M = 1e2 + 5;
const int MOD = 20071027;
char text[L], word[M];
struct Trie	{
	int ch[N*M][26], val[N*M], sz;
	int idx(char c)	{
		return c - 'a';
	}
	void clear(void)	{
		sz = 1;	memset (ch[0], 0, sizeof (ch[0]));
	}
	void insert(char *str, int v)	{
		int u = 0,  len = strlen (str);
		for (int c, i=0; i<len; ++i)	{
			c = idx (str[i]);
			if (!ch[u][c])	{
				memset (ch[sz], 0, sizeof (ch[sz]));
				val[sz] = 0;
				ch[u][c] = sz++;
			}
			u = ch[u][c];
		}
		val[u] = v;
	}
	void query(char *str, int len, vector<int> &vec)	{
		int u = 0;
		for (int c, i=0; i<len; ++i)	{
			c = idx (str[i]);
			if (!ch[u][c])	return ;
			u = ch[u][c];
			if (val[u])	vec.push_back (val[u]);
		}
	}
}trie;
int dp[L], l[N];

int main(void)	{
	int cas = 0, n;
	while (scanf ("%s", &text) == 1)	{
		scanf ("%d", &n);
		trie.clear ();
		for (int i=1; i<=n; ++i)	{
			scanf ("%s", &word);
			l[i] = strlen (word);
			trie.insert (word, i);
		}
		memset (dp, 0, sizeof (dp));
		int len = strlen (text);	dp[len] = 1;
		for (int i=len-1; i>=0; --i)	{
			vector<int> vec;
			trie.query (text+i, len-i, vec);
			for (int j=0; j<vec.size (); ++j)	{
				dp[i] = (dp[i] + dp[i+l[vec[j]]]) % MOD;
			}
		}
		printf ("Case %d: %d
", ++cas, dp[0]);
	}

	return 0;
}

  

编译人生,运行世界!
原文地址:https://www.cnblogs.com/Running-Time/p/5071515.html