LA 3942 Remember the Word

        题意:给出一个由s个不同的单词组成的字典和一个长字符串str。把这个字符串分解成若干个单词的连接(可重复使用),求有多少种不同的分法。

        思路:字典树+dp。用字典树去匹配单词,并dp计数。dp(x)表示从str[x]到末尾的分解方法数。如果str[i]~str[j]正好与某个单词相匹配,那么dp(i)就应该加上dp(j)。字典树的建树主要参考了大白书。


#include <iostream>           
#include <stdio.h>           
#include <cmath>           
#include <algorithm>           
#include <iomanip>           
#include <cstdlib>           
#include <string>           
#include <memory.h>           
#include <vector>           
#include <queue>           
#include <stack>           
#include <map>         
#include <set>         
#include <ctype.h>           
#define INF 1<<30       
#define ll long long       
#define max3(a,b,c) max(a,max(b,c))       
#define MAXA 100000  
  
using namespace std;  

char text[300010];
char word[4010][110];
int dp[600010];

struct Trie{
	int ch[300010][26];
	int val[300010];
	int sz;
	
	void init(){
		sz=1;
		memset(ch[0],0,sizeof(ch[0]));
	}
	
	void insert(char* str,int v){
		int u=0;
		int len=strlen(str);
		for(int i=0;i<len;i++){
			if(!ch[u][str[i]-'a']){
				memset(ch[sz],0,sizeof(ch[sz]));
				val[sz]=0;
				ch[u][str[i]-'a']=sz++;
			}
			u=ch[u][str[i]-'a'];
		}
		val[u]=v;
	}
	
	bool sear(char* str,int s,int len){
		int u=0;
		for(int i=0;i<len;i++){
			if(!ch[u][str[i]-'a'])return 0;
			u=ch[u][str[i]-'a'];
			if(val[u]){
				dp[s]+=dp[s+i+1];
				dp[s]%=20071027;
			}
		}
		if(!val[u])return 0;
		return 1;
	}
};

Trie root;

int main(){
	int cas=0;
	while(~scanf("%s",text)){
		root.init();
		cas++;
		memset(dp,0,sizeof(dp));
		
		int n;
		cin>>n;
		
		for(int i=1;i<=n;i++){
			scanf("%s",word[i]);
			root.insert(word[i],1);
		}
		
		int end=strlen(text);
		dp[end]=1;
		for(int i=end-1;i>=0;i--){
			root.sear(text+i,i,end-i);
		}
		printf("Case %d: %d\n",cas,dp[0]);
	}
	return 0;
}


你可能感兴趣的:(字典树)