Neal is very curious about combinatorial problems, and now here comes a problem about words. Knowing that Ray has a photographic memory and this may not trouble him, Neal gives it to Jiejie.
Since Jiejie can't remember numbers clearly, he just uses sticks to help himself. Allowing for Jiejie's only 20071027 sticks, he can only record the remainders of the numbers divided by total amount of sticks.
The problem is as follows: a word needs to be divided into small pieces in such a way that each piece is from some given set of words. Given a word and the set of words, Jiejie should calculate the number of ways the given word can be divided, using the words in the set.
Input
The input file contains multiple test cases. For each test case: the first line contains the given word whose length is no more than 300 000.
The second line contains an integer S,
1
S
4000.
Each of the following S lines contains one word from the set. Each word will be at most 100 characters long. There will be no two identical words and all letters in the words will be lowercase.
There is a blank line between consecutive test cases.
You should proceed to the end of file.
Output
For each test case, output the number, as described above, from the task description modulo 20071027.
Sample Input
abcd 4 a b cd ab
Sample Output
Case 1: 2
给一个字符串和一些单词,这个字符串由这些单词组成的情况有多少种。
若正着推,dp[i]代表前i个字母能由单词组成的情况有多少种,那么dp[i]=sum{dp[i-len],str[i-len+1]...str[i]是一个单词}。如果对于每个i都枚举一遍所有单词会超时,用前缀树找从s[i]开始到结尾的这个路径,中途若遇到单词标记就可以进行一次dp,若位置str[j]被标记,则更新sum[j]=(sum[j]+sum[i-1])%MOD。注意这是用前面的状态更新后面的状态。若倒着推,设dp[i]为后i个字母能由单词组成的情况有多少种,dp倒着进行,则sum[i]=(sum[i]+sum[j+1])%MOD,比较符合一般DP的思路。
可以在前缀树查找的过程中完成dp,也可以查找的时候把点记下来,查找完后再dp。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 300010
#define MAXNODE 400010
#define MOD 20071027
#define INF 0x3f3f3f3f
using namespace std;
int N,sum[MAXN];
char str[MAXN];
struct Tire{
int ch[MAXNODE][26];
int val[MAXNODE];
int sz;
Tire(){
memset(ch,0,sizeof(ch));
memset(val,0,sizeof(val));
sz=1;
}
int get_id(char c){
return c-'a';
}
void insert(char *s,int v){
int u=0;
for(int i=0;s[i];i++){
int c=get_id(s[i]);
if(!ch[u][c]) ch[u][c]=sz++;
u=ch[u][c];
}
val[u]=1;
}
void query(char *s){
int u=0;
for(int i=1;s[i];i++){
int u=0;
for(int j=i;s[j];j++){
int c=get_id(s[j]);
if(!ch[u][c]) break;
u=ch[u][c];
if(val[u]) sum[j]=(sum[j]+sum[i-1])%MOD;
}
}
}
};
int main(){
freopen("in.txt","r",stdin);
while(scanf("%s",str+1)!=EOF){
scanf("%d",&N);
Tire tire;
char s[110];
for(int i=1;i<=N;i++){
scanf("%s",s);
tire.insert(s,1);
}
tire.query(str);
printf("%d\n",sum[strlen(str+1)]);
}
return 0;
}
本文探讨了一道关于字符串划分的组合问题,并介绍了一种利用前缀树优化搜索过程的有效解决方案。通过正向和逆向两种不同的动态规划方法,文章详细解释了如何计算一个字符串可以由给定单词集组成的方案数量。
441

被折叠的 条评论
为什么被折叠?



