Remember the Word UVA - 1401(传送门)
题意:
给你一个字典。和一个模式串。
现在要求你按照字典分割模式串。
求:
方案数。
思路:
很容易想到递推的思想。
之后就是怎样递推。
这里为了使用trie,所以倒着dp。
dp【i】=dp【i】+dp【i+len【x】】。(这里表示对于s【i,len】中,找到一个前缀,这个前缀在字典里,之后转移。len【x】表示这个前缀的长度)
AC
#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
#define For(i,x,y) for(int i=(x); i<=(y); i++)
using namespace std;
const int maxnode=4000*100+10;///最多有多少层
const int sigma_size=26;///字母的话就是26
const int maxn=4000+10;
const int maxm=3e5+10;
const int mod=20071027;
int len[maxn];
char text[maxm];
struct Tire{
int ch[maxnode][sigma_size];///ch[u][0]代表这个结点不存在
int val[maxnode];///结点的权值(相当于标记是否为一个字符串的最后一个元素)
int sz;///结点总数
void clear(){sz=1; memset(ch[0],0,sizeof(ch[0])); }
// Tire(){sz=1; memset(ch[0],0,sizeof(ch[0])); }///初始化第一个根结点
int idx(char c){return c-'a';}
//
///插入字符串s,附加信息v(权值)。注意v必须非0,因为0代表“本结点不是单词结点”
void insert(char *s, int v){
int u=0, len_s=strlen(s);
for(int i=0; i<len_s; i++){
int c=idx(s[i]);
if(!ch[u][c]){///结点不存在
memset(ch[sz],0,sizeof(ch[sz]));
val[sz]=0;///中间结点的附加信息为0.
ch[u][c]=sz++;///新建结点
}
u=ch[u][c];///往下走。
//cout<<u<<endl;
}
val[u]=v;///最后一个字符的附加信息为v。
}
void find_pre(char *s, int len_s, vector<int>&ans){
int u=0;
for(int i=0; i<len_s; i++){
int c=idx(s[i]);
if(!ch[u][c])break;
u=ch[u][c];
if(val[u])ans.push_back(val[u]);
}
}
}tire;
void init(){
int num;
scanf("%d", &num);
For(i,1,num){
char s[200];
scanf("%s", s);
tire.insert(s,i);
len[i]=strlen(s);
}
}
int dp[maxm];
int work(){
int len_text=strlen(text);
dp[len_text]=1;
for(int i=len_text-1; i>=0; i--){
vector<int>p;dp[i]=0;
tire.find_pre(text+i, len_text-i,p);
//cout<<"p.size()-----"<<p.size()<<endl;
for(int j=0; j<p.size(); j++)dp[i]=(dp[i]+dp[ i+len[p[j]] ])%mod;
//cout<<"dp[i]------"<<dp[i]<<endl;
}
return dp[0];
}
int main()
{
int kase=0;
while(~scanf("%s", text)){
tire.clear();
init();
printf("Case %d: %d\n", ++kase,work());
}
return 0;
}
/*
abcd
4
a
b
cd
ab
*/