给出一个字符串和一些字符片段,求由这些字符片段组成这个字符串可以有多少种组的方法。
Sample Input
abcd
4
a
b
cd
ab
Sample Output
Case 1: 2
明显 对于字符串上的每个位置而言,如果用dp【i】代表从i开始的后缀最多有多少种组合方式的话,dp【i】 = sum(dp【i】 + len(x)| 这里的x代表从i开始的后缀的第一个组成部分的长度);
如果直接枚举的话最坏的情况要 每次匹配4000种 每次匹配的过程中又要花费时间 .
直接在树上查找的话最多100次就可以判断所有的情况。
明显 对于字符串上的每个位置而言,如果用dp【i】代表从i开始的后缀最多有多少种组合方式的话,dp【i】 = sum(dp【i】 + len(x)| 这里的x代表从i开始的后缀的第一个组成部分的长度);
如果直接枚举的话最坏的情况要 每次匹配4000种 每次匹配的过程中又要花费时间 .
直接在树上查找的话最多100次就可以判断所有的情况。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>
using namespace std;
const int maxn = 300000+10;
const int mod = 20071027;
char s[maxn],s1[maxn];
int ch[maxn][30];
int val[maxn];
int dp[maxn];
int idx( char ch ) {return ch-'a';} // 字母的编号
struct trie
{
int sz;
trie(){sz=1;memset(ch[0],0,sizeof(ch[0]));}
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]));
ch[u][c] = sz++;
}
u = ch[u][c];// 向下
}
val[u] = v; // 在一个字符段结束时打上标记
}
int Find(char *s,int pos)
{
int u = 0, n = strlen(s);
int ans = 0;
for(int i=pos;i<n&&i-pos<=100;i++) // 在后缀中查找能出现的所有的字符段
{
int c = idx(s[i]);
if(!ch[u][c]) return ans;
u = ch[u][c];
if(val[u]) // 是一个字符段的结束
{
ans = (ans + dp[i+1]) % mod;
}
}
return ans;
}
};
int m;
int main()
{
int kase = 1;
while(scanf("%s",s)!=EOF)
{
memset(dp,0,sizeof(dp));
memset(ch,0,sizeof(ch));
memset(val,0,sizeof(val));
// cout << s <<endl;
trie tree = trie();
// getchar();
cin >> m;
for(int i=0;i<m;i++)
{
scanf("%s",s1);
// cout << s1 << endl;
tree.Insert(s1,1);
}
int n = strlen(s);
dp[n] = 1;
for(int i=n-1;i>=0;i--)
{
dp[i] = tree.Find(s,i);
}
printf("Case %d: ",kase++);
cout << dp[0] << endl;
}
return 0;
}