题目:BZOJ4861.
题目大意:给定
n
n
n个串
S
i
S_i
Si和
m
m
m个串
T
i
T_i
Ti,现在要用这
S
i
S_i
Si个串(可重复使用)拼成一个长度为
l
l
l的串使得拼成的串所有
T
i
T_i
Ti均未出现,求方案数.注意只要组成方式不同就算不同的方案.
1
≤
n
,
m
≤
50
,
1
≤
∑
∣
S
i
∣
,
∑
∣
T
i
∣
≤
100
,
1
≤
l
≤
1
0
8
1\leq n,m\leq 50,1\leq \sum |S_i|,\sum |T_i|\leq 100,1\leq l\leq 10^8
1≤n,m≤50,1≤∑∣Si∣,∑∣Ti∣≤100,1≤l≤108.
数据分成两档,第一档满足
1
≤
l
≤
100
1\leq l\leq 100
1≤l≤100,第二档满足
1
≤
∣
S
i
∣
≤
2
1\leq |S_i|\leq 2
1≤∣Si∣≤2.
一题正解分两段是真的恶心,强行二合一啊…
首先,看到这种套路题肯定先把所有 T i T_i Ti放进Trie里建AC自动机.
然后考虑一个DP,设 f [ i ] [ j ] f[i][j] f[i][j]表示构造一个长度为 i i i的串,在AC自动机中运行到点 j j j,且不能有任何一个 T i T_i Ti出现的方案数.
现在我们考虑转移,显然可以枚举每一个串 S k S_k Sk,若在AC自动机上从状态 j j j开始运行串 S k S_k Sk到达点 t t t,则 f [ i ] [ j ] f[i][j] f[i][j]可以转移到 f [ i + ∣ S k ∣ ] [ t ] f[i+|S_k|][t] f[i+∣Sk∣][t].
也就是说:
f
[
i
]
[
j
]
→
f
[
i
+
∣
S
k
∣
]
[
t
=
j
→
S
k
]
f[i][j]\rightarrow f[i+|S_k|][t=j\rightarrow S_k]
f[i][j]→f[i+∣Sk∣][t=j→Sk]
我们先处理出所有不能走到的点(每个 T i T_i Ti代表的状态在fail树上的整棵子树),在运行时若走到了这么一个点就直接把贡献设为 0 0 0就好了.
发现这个算法的时间复杂度为 O ( l ∑ ∣ T i ∣ ∑ ∣ S i ∣ ) O(l\sum |T_i|\sum |S_i|) O(l∑∣Ti∣∑∣Si∣),可以通过第一档数据.
那么第二档数据呢?我们发现这个时候 l l l很大,但是 S i S_i Si的长度均小于等于 2 2 2,这不是明摆着矩乘么…
但是若 S i S_i Si的长度为 1 1 1还好做, S i S_i Si的长度为 2 2 2怎么做?
其实很简单,原来我们的状态矩阵只记录了当前串长为 i i i时的信息,现在只需要多记录串长为 i − 1 i-1 i−1时的信息就好了.
时间复杂度 O ( ( ∑ ∣ T i ∣ ) 3 log l ) O((\sum |T_i|)^3\log l) O((∑∣Ti∣)3logl),可以通过第二档数据.
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=50,M=100,C=26,mod=1000000007;
int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
int sub(int a,int b){return a-b<0?a-b+mod:a-b;}
int mul(int a,int b){return (LL)a*b%mod;}
void sadd(int &a,int b=1){a=add(a,b);}
void ssub(int &a,int b=1){a=sub(a,b);}
void smul(int &a,int b){a=mul(a,b);}
int n,m,sk,len[N+9];
char s[N+9][M+9],t[M+9];
struct Trie{
int s[C],fail,cnt;
}tr[M+9];
int cn;
void Build(){cn=1;}
int Insert(char *s,int n){
int k=1;
for (int i=1;i<=n;++i)
if (tr[k].s[s[i]-'a']) k=tr[k].s[s[i]-'a'];
else tr[k].s[s[i]-'a']=++cn,k=cn;
++tr[k].cnt;
return k;
}
queue<int>q;
void Bfs_fail(){
tr[1].fail=1;
for (int i=0;i<C;++i)
if (tr[1].s[i]) tr[tr[1].s[i]].fail=1,q.push(tr[1].s[i]);
else tr[1].s[i]=1;
while (!q.empty()){
int t=q.front();q.pop();
tr[t].cnt+=tr[tr[t].fail].cnt;
for (int i=0;i<C;++i)
if (tr[t].s[i]) tr[tr[t].s[i]].fail=tr[tr[t].fail].s[i],q.push(tr[t].s[i]);
else tr[t].s[i]=tr[tr[t].fail].s[i];
}
}
int Judge(int st,char *s,int n){
if (tr[st].cnt) return 0;
int k=st;
for (int i=1;i<=n;++i){
k=tr[k].s[s[i]-'a'];
if (tr[k].cnt) return 0;
}
return k;
}
int dp1[M+9][M+9],ans;
struct matrix{
int n,m,v[M*2+9][M*2+9];
matrix operator * (const matrix &p)const{
matrix res=matrix();
res.n=n;res.m=p.m;
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
for (int k=1;k<=p.m;++k)
sadd(res.v[i][k],mul(v[i][j],p.v[j][k]));
return res;
}
}dp2,go;
void Build_dp2(){
dp2.n=1;dp2.m=cn<<1;
dp2.v[1][cn+1]=1;
for (int i=1;i<=n;++i)
if (len[i]==1){
int td=Judge(1,s[i],1);
if (!td) continue;
sadd(dp2.v[1][td]);
}
}
void Build_go(){
go.n=go.m=cn<<1;
for (int i=1;i<=n;++i)
if (len[i]==1){
for (int j=1;j<=cn;++j){
int td=Judge(j,s[i],1);
if (td) sadd(go.v[j][td]);
}
}else{
for (int j=1;j<=cn;++j){
int td=Judge(j,s[i],2);
if (td) sadd(go.v[j+cn][td]);
}
}
for (int i=1;i<=cn;++i) go.v[i][i+cn]=1;
}
matrix E(int n){
matrix res=matrix();
res.n=res.m=n;
for (int i=1;i<=n;++i) res.v[i][i]=1;
return res;
}
matrix Power(matrix a,int k){
matrix res=E(a.n);
for (;k;k>>=1,a=a*a)
if (k&1) res=res*a;
return res;
}
Abigail into(){
scanf("%d%d%d",&n,&m,&sk);
for (int i=1;i<=n;++i){
scanf("%s",s[i]+1);
len[i]=strlen(s[i]+1);
}
Build();
for (int i=1;i<=m;++i){
scanf("%s",t+1);
Insert(t,strlen(t+1));
}
}
Abigail work1(){
Bfs_fail();
dp1[0][1]=1;
for (int i=0;i<sk;++i)
for (int j=1;j<=cn;++j){
if (!dp1[i][j]) continue;
for (int k=1;k<=n;++k){
if (i+len[k]>sk) continue;
int td=Judge(j,s[k],len[k]);
if (!td) continue;
sadd(dp1[i+len[k]][td],dp1[i][j]);
}
}
for (int i=1;i<=cn;++i) sadd(ans,dp1[sk][i]);
}
Abigail work2(){
Bfs_fail();
Build_dp2();
Build_go();
dp2=dp2*Power(go,sk-1);
for (int i=1;i<=cn;++i) sadd(ans,dp2.v[1][i]);
}
Abigail outo(){
printf("%d\n",ans);
}
int main(){
into();
if (sk<=M) work1();
else work2();
outo();
return 0;
}