LA 3942 - Remember the Word 字典树+DP

看题传送门:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1943


给出一个由S个不同单词组成的字典和一个长字符串,把这个字符串分解成若干个单词的连接(单词可以重复使用),有多少种方法?比如有4个单词a 、b 、cd、ab,则abcd有两种分解方法:a+b+cd和ab+cd


用DP来做的话,设dp[i]表示从i开始的字符串到结束的分解方案数,则d[i]=sum{d[ i + len(x)] 单词x是 S[i ……len-1]的前缀。。

故从右向左看,枚举前缀,如果有这个单词则加上d[i]。


PS:用数组实现的话会更快点,我觉得指针好写。。。还有就是这题不释放空间也可以过。(也更快点)。

#include<cstdio>
#include<cstring>
const int MAXN=300000+10;
const int MLEN=26;
const int mod=20071027;
char s[MAXN],word[MAXN];
int dp[MAXN];

struct node
{
	node* next[MLEN];
	bool isEnd;
	node(){ memset(next,0,sizeof(next)); isEnd=false;}
};

struct Trie
{
	node *root;
	
	inline int index(char &c){return c-'a';}

	Trie() {root=new node;}
	void init()  {root=new node;}
	void insert(char *str)
	{
		node *p=root;
		int len=strlen(str);
		for(int i=0;i<len;i++)
		{
			int id=index(str[i]);
			if(p->next[id]==NULL)
			{
				node *t=new node;
				p->next[id]=t;
			}		
			p=p->next[id];

			if(i==len-1)
			{
				p->isEnd=true;
				return;
			}		
		}
	}

	void query(char *str,int start)
	{
		int len=strlen(str);
		node *p=root;
		int res=0;
		for(int i=start;i<len;i++)
		{
			int id=index(str[i]);
			if(p->next[id]==NULL)
				break;

			p=p->next[id];
			if(p->isEnd)
			{
				res+=dp[i+1];
				res%=mod;
			}
		}
		dp[start]=res;
	}

	void del(node *root)
	{
		if(root==NULL)
			return;

		for(int i=0;i<26;i++)
			if(root->next[i]!=0)
				del(root->next[i]);
		
		delete root;
	}
}trie;

int main()
{
	int kase=1;
	while(scanf("%s",s)!=EOF)
	{
		//初始化trie
		memset(dp,0,sizeof(dp));
		trie.init();

		int n;
		scanf("%d",&n);
		while(n--)
		{
			scanf("%s",word);
			trie.insert(word);
		}
		int len=strlen(s)-1;
		dp[len+1]=1;
		for(int i=len;i>=0;i--)
		{
			trie.query(s,i);
		//	printf("%d\n",dp[i]);
		}
		printf("Case %d: %d\n",kase++,dp[0]);
		trie.del(trie.root);
	}
}



你可能感兴趣的:(LA 3942 - Remember the Word 字典树+DP)