3942 - Remember the Word
题意:
给一个主串 S S S,给 n n n个串 T i T_i Ti,每个 T i T_i Ti可以用无数次,求用 T i T_i Ti组成 S S S的方法数。
思路:
显然可以用
d
p
dp
dp递推求解,暴力递推肯定
T
L
E
TLE
TLE
考虑将
n
n
n个
T
i
T_i
Ti做成字典树后,从后往前利用前缀转移
d
p
[
i
]
=
∑
d
p
[
i
+
l
e
n
(
x
)
]
∗
c
n
t
[
x
]
dp[i]=\sum dp[i+len(x)]*cnt[x]
dp[i]=∑dp[i+len(x)]∗cnt[x] (
x
x
x为匹配的串,
c
n
t
[
x
]
cnt[x]
cnt[x]为
x
x
x的个数)
那么对于每一个
d
p
[
i
]
dp[i]
dp[i],每次从
T
r
i
e
Trie
Trie树上走一遍即可
复杂度
O
(
m
a
x
(
l
e
n
[
T
i
]
)
∗
l
e
n
[
s
]
)
O(max(len[T_i])*len[s])
O(max(len[Ti])∗len[s])
/* Author : Rshs
* Data : 2019-09-17-17.04
*/
#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const double pai = acos(-1);
const double eps = 1e-10;
const LL mod = 20071027;
const int MX = 1e6+5; //字符最长长度
int trie[MX][28]; //i节点,j字符的编号
int col[MX]; //树上信息,(字符串个数)
int nonum;
void ins(char *x){ //插入
int le=strlen(x),p=0;
for(int i=0;i<le;i++){
int c=x[i]-'a';
if(!trie[p][c]){ //没的话新建
trie[p][c]=++nonum;
}
p=trie[p][c];
}
col[p]++; //字符串x数量++
}
int sear(char *x){ //查找字符串x的数量
int le=strlen(x),p=0;
for(int i=0;i<le;i++){
int c=x[i]-'a';
if(!trie[p][c]) return 0;
p=trie[p][c];
}
return col[p];
}
char s[300005],t[4005][105];int n;
LL dp[300005];
int main(){
int cas=1;
while(~scanf("%s",s)){
memset(trie,0,sizeof(trie));
memset(col,0,sizeof(col));
int n;scanf("%d",&n);getchar();
for(int i=1;i<=n;i++){
scanf("%s",t[i]);
ins(t[i]);
}
int le=strlen(s);
dp[le]=1;
for(int i=le-1;i>=0;i--){ //因为前缀建树,所以逆序求dp
int p=0;dp[i]=0;
for(int j=i;j<le;j++){
int c=s[j]-'a';
if(trie[p][c]==0) break;
p=trie[p][c];
dp[i]=(dp[i]+dp[j+1]*col[p]%mod)%mod;
}
}
printf("Case %d: %lld\n",cas++,dp[0]);
}
return 0;
}