title
solution
针对数据编程才是坠吊的!!!
观察数据,发现分隔数据的
L
L
L跨度过大,没有衔接——推测很有可能是分数据做法
①:考虑
L
≤
100
L\le100
L≤100的情况
可以暴力
D
P
DP
DP转移
设
d
p
[
i
]
[
j
]
:
dp[i][j]:
dp[i][j]: 长度为
i
i
i指向自动机上
j
j
j节点的方案数
枚举基本串
k
k
k,写个
f
i
n
d
(
)
find()
find()函数暴力找
j
j
j节点跳基本串
k
k
k后指向的节点
t
o
to
to
d
p
[
i
+
l
e
n
[
k
]
]
[
t
o
]
=
(
d
p
[
i
+
l
e
n
[
k
]
]
[
t
o
]
+
d
p
[
i
]
[
j
]
)
%
m
o
d
dp[i+len[k]][to]=(dp[i+len[k]][to]+dp[i][j])\%mod
dp[i+len[k]][to]=(dp[i+len[k]][to]+dp[i][j])%mod
O
(
L
3
)
O(L^3)
O(L3)完全可以,非常可以,很好很好
②:考虑基本串的长度只有 1 / 2 1/2 1/2
思考一:
此时
L
L
L急速飙升,一般上了
1
e
7
,
1
e
8
,
1
e
9
1e7,1e8,1e9
1e7,1e8,1e9的数据点不是有规律周期可循,就是
n
,
l
o
g
n
\sqrt n,logn
n,logn
这里因为长度特别小,加上之前的做题经验,知道
A
C
AC
AC自动机套矩阵快速幂的套路
于是自然而然就往矩阵加速方面靠了
思考二:
对于长度为
l
e
n
len
len的一个串
s
s
s,只有可能是由长度为
s
−
1
s-1
s−1的一个固定的串
s
′
s'
s′,或者长度为
s
−
2
s-2
s−2的一个固定的串
s
′
′
s''
s′′转移而来
如果借用上面的
d
p
dp
dp思路,应该
d
p
[
l
e
n
]
[
t
o
]
=
(
d
p
[
l
e
n
−
1
]
[
j
]
+
d
p
[
l
e
n
−
2
]
[
k
]
)
%
m
o
d
dp[len][to]=(dp[len-1][j]+dp[len-2][k])\%mod
dp[len][to]=(dp[len−1][j]+dp[len−2][k])%mod
这个狮子不仅代表的是转移方程,也在暗示每个状态只会与之前的最多两个状态挂钩
眼尖的犇犇更会发现有点像斐波拉契递推式——此时也会联想到矩阵加速求解
有长度为
1
1
1的基本串,也有长度为
2
2
2的基本串,矩阵要扩大两倍
也可以理解为把
N
×
N
N\times N
N×N的一个矩阵看成一项
f
i
f_i
fi,可以类比斐波拉契的转移推导过程
PS:下面的 n n n仅代表一个字符,含义并不是题目里的 n n n
初始矩阵
O
O
O
(
x
0
x
1
x
2
.
.
x
n
x
0
′
x
1
′
.
.
x
n
′
)
\begin{pmatrix} x_0\\ x_1\\ x_2\\ .\\ .\\ x_n\\ x_0'\\ x_1'\\ .\\ .\\ x_n' \end{pmatrix}
⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎛x0x1x2..xnx0′x1′..xn′⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎞
我们想让ta转移成
(
x
0
′
x
1
′
x
2
′
.
.
x
n
′
x
0
′
′
x
1
′
′
.
.
x
n
′
′
)
\begin{pmatrix} x_0'\\ x_1'\\ x_2'\\ .\\ .\\ x_n'\\ x_0''\\ x_1''\\ .\\ .\\ x_n'' \end{pmatrix}
⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎛x0′x1′x2′..xn′x0′′x1′′..xn′′⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎞
code
#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
#define int long long
#define mod 1000000007
int n, m, L, tot;
char s[105];
int len[55];
int dp[105][105];
char S[55][105];
queue < int > q;
struct node {
int trie[105][30], fail[105];
bool End[105];
void insert() {
int now = 0, Len = strlen( s );
for( int i = 0;i < Len;i ++ ) {
int nxt = s[i] - 'a';
if( ! trie[now][nxt] ) trie[now][nxt] = ++ tot;
now = trie[now][nxt];
}
End[now] = 1;
}
void Fail() {
memset( fail, 0, sizeof( fail ) );
while( ! q.empty() ) q.pop();
fail[0] = 0;
for( int i = 0;i < 26;i ++ )
if( trie[0][i] ) {
fail[trie[0][i]] = 0;
q.push( trie[0][i] );
}
while( ! q.empty() ) {
int now = q.front(); q.pop();
End[now] |= End[fail[now]];
for( int i = 0;i < 26;i ++ ) {
if( trie[now][i] ) {
fail[trie[now][i]] = trie[fail[now]][i];
q.push( trie[now][i] );
}
else
trie[now][i] = trie[fail[now]][i];
}
}
}
int find( int now, int id ) {//求从now开始跳id串后指向的节点
for( int i = 0;i < len[id];i ++ ) {
now = trie[now][S[id][i] - 'a'];
if( End[now] ) return -1;
}
return now;
/*
这种写法是错误的
从End[now]处理的是从根节点到now这一条串的路上是否含有一个被禁止的子串
从被禁止的子串开始往下跳不算
for( int i = 0;i < len[id];i ++ ) {
if( End[now] ) return -1;
else now = trie[now][S[id][i] - 'a'];
}
if( End[now] ) return -1;
else return now;
*/
}
}AC;
struct Matrix {
int c[205][205];
Matrix() {
memset( c, 0, sizeof( c ) );
}
Matrix operator * ( const Matrix &p ) const {
Matrix ans;
for( int i = 0;i <= ( tot << 1 | 1 );i ++ )
for( int k = 0;k <= ( tot << 1 | 1 );k ++ )
if( c[i][k] )
for( int j = 0;j <= ( tot << 1 | 1 );j ++ )
ans.c[i][j] = ( ans.c[i][j] + c[i][k] * p.c[k][j] % mod ) % mod;
return ans;
}
}O, V;
Matrix qkpow( Matrix x, int y ) {
Matrix ans;
for( int i = 0;i <= ( tot << 1 | 1 );i ++ )
ans.c[i][i] = 1;
while( y ) {
if( y & 1 ) ans = ans * x;
x = x * x;
y >>= 1;
}
return ans;
}
//dp[i][j]:长度为i指向自动机上j的方案数
void subtask1() {
dp[0][0] = 1;
for( int i = 0;i <= L;i ++ ) {
for( int j = 0;j <= tot;j ++ ) {
if( ! dp[i][j] ) continue;
for( int k = 1;k <= n;k ++ ) {
int to = AC.find( j, k );
if( ! ( ~ to ) || i + len[k] > L ) continue;
else dp[i + len[k]][to] = ( dp[i + len[k]][to] + dp[i][j] ) % mod;
}
}
}
int ans = 0;
for( int i = 0;i <= tot;i ++ ) ans = ( ans + dp[L][i] ) % mod;
printf( "%lld\n", ans );
}
void subtask2() {
O.c[0][0] = 1;
for( int i = 0;i <= tot;i ++ )
V.c[i + tot + 1][i] = 1;
for( int i = 0;i <= tot;i ++ ) {
for( int j = 1;j <= n;j ++ ) {
int to = AC.find( i, j );
if( ! ( ~ to ) ) continue;
if( len[j] & 1 ) {
V.c[i + tot + 1][to + tot + 1] ++;
if( ! i ) O.c[0][to + tot + 1] ++;
}
else
V.c[i][to + tot + 1] ++;
}
}
O = O * qkpow( V, L );
int ans = 0;
for( int i = 0;i <= tot;i ++ )
ans = ( ans + O.c[0][i] ) % mod;
printf( "%lld\n", ans );
}
signed main() {
scanf( "%lld %lld %lld", &n, &m, &L );
for( int i = 1;i <= n;i ++ )
scanf( "%s", S[i] ), len[i] = strlen( S[i] );
for( int i = 1;i <= m;i ++ ) {
scanf( "%s", s );
AC.insert();
}
AC.Fail();
if( L <= 100 ) subtask1();
else subtask2();
return 0;
}