UVAlive 3942 Remember the Word

 Time Limit: 3000MS Memory Limit: Unknown 64bit IO Format: %lld & %llu

Description

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 , 1S4000 .

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

``` #include<cstdio>
#include<cstring>
#include<vector>
#define FOR(a,b,c) for(int a=(b);a<(c);a++)
using namespace std;

const int maxnode =  *  + ;
const int sigma_size = ;

// 字母表为全体小写字母的Trie
struct Trie {
int ch[maxnode][sigma_size];
int val[maxnode];
int sz;
void clear() { sz = ; memset(ch[], , sizeof(ch[])); }
int idx(char c) { return c - 'a'; }

void insert(const char *s, int v) {
int u = , n = strlen(s);
for(int i = ; i < n; i++) {
int c = idx(s[i]);
if(!ch[u][c]) {
memset(ch[sz], , sizeof(ch[sz]));
val[sz] = ;
ch[u][c] = sz++;
}
u = ch[u][c];
}
val[u] = v;
}

// 找字符串s的长度不超过len的前缀
void find_prefixes(const char *s, int len, vector<int>& ans) { //构造ans
int u = ;
for(int i = ; i < len; i++) {
if(s[i] == '\0') break;
int c = idx(s[i]);
if(!ch[u][c]) break;       //不存在该字符 查找失败退出
u = ch[u][c];              //否则往下找
if(val[u] != ) ans.push_back(val[u]); // 找到一个前缀
}
}
//find_prefixes操作以vector形式返回满足要求的字串标号val
//find操作需要对查找操作进行长度限制 以节省时间
};

const int maxl=  + ;
const int maxw= +;    //单词个数
const int maxwl= + ;
const int MOD = ;

char text[maxl];
int d[maxl],len[maxw];
Trie trie;

int main(){
int kase=,n;
while(scanf("%s%d",text,&n)==){
trie.clear();   //clear()
char word[maxwl];
FOR(i,,n+){   //标号为1..n
scanf("%s",word);
len[i]=strlen(word);    //记录word长度 节约了一个char[][]
trie.insert(word,i);   //构造Trie val为word标号
}

// DP
memset(d,,sizeof(d));
int L=strlen(text);
d[L]=;   // ? :DP边界 只要后缀中有前缀单词 则 += 1
for(int i=L-;i>=;i--){
vector<int> p;                     //存储
trie.find_prefixes(text+i,L-i,p);
//寻找在后缀text+i中以前缀形势出现过的所有单词 //text+i为字串text[i..L)
FOR(j,,p.size())
d[i] = (d[i]+d[i+len[p[j]]]) % MOD;  //i+len-1为单词尾
}
printf("Case %d: %d\n",++kase,d[]);
}
return ;
} ```