题意:给出N个不同单词和一个长字符串S。把这个字符串分解成若干个单词的连接(单词尅重复使用),问有多少种方法?
分析:令d[i]表示从字符i开始的字符串的分解方案数,则d[i]=sum{d[i+len[x]] | 单词x是S[i...len]的前缀};
详看《算法竞赛入门经典》---刘汝佳--Page 208..
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<vector>
using namespace std;
#define sigma_size 26
#define maxnode 400010 //节点个数
#define MOD 20071027
struct Trie//Trie模板
{
int ch[maxnode][sigma_size];//maxnode表示节点个数,每个节点有26种情况,所以ch数组能表示所有情况
int val[maxnode]; //存节点的信息
int sz; //节点总个数
void clear(){sz=1; memset(ch[0],0,sizeof(ch[0]));}//初始化
int idx(char c){ return c - 'a'; }
void insert(char *s, int v)
{
int u=0,n=strlen(s);
for(int i=0;i<n;i++){
int c=idx(s[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 find(char *s,int len,vector<int>& ans)
{
int u=0;
for(int i=0;i<len;i++)
{
int c=idx(s[i]);
if(ch[u][c]){
u=ch[u][c];
if(val[u])
ans.push_back(val[u]);
}
else
break;
}
}
};
Trie node;
char sh[300010];
int d[4005];
int sum[300010];
int main()
{
int i=0,j,k;
int t=0;
while(~scanf("%s",sh))
{
t++;
int n;
node.clear();
char p[105];
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%s",p);
node.insert(p,i);
d[i]=strlen(p);
}
int len=strlen(sh);
memset(sum,0,sizeof(sum));
sum[len]=1;
for(i=len-1;i>=0;i--){
vector<int> ans;
node.find(sh+i,len-i,ans);
for(j=0;j<ans.size();j++)
{
sum[i]=(sum[i]+sum[i+d[ans[j]]])%MOD;
}
}
printf("Case %d: %d\n",t,sum[0]);
}
return 0;
}