T1 P3193 [HNOI2008]GT考试
思路:
设
f
i
,
j
f_{i,j}
fi,j 表示有
i
i
i 个号码,kmp 上匹配到第
j
j
j 位时的情况总数
g
i
,
j
g_{i,j}
gi,j 表示 kmp 上第
i
i
i 个位置转移到第
j
j
j 个位置的总方案数
有 dp 方程:
f
i
,
j
=
∑
k
=
0
m
f
i
−
1
,
k
×
g
k
,
j
f_{i,j}=\sum_{k=0}^{m} f_{i-1,k}\times g_{k,j}
fi,j=k=0∑mfi−1,k×gk,j
时间复杂度:
O
(
n
)
O(n)
O(n)
考虑优化
发现
f
f
f 的第一维是递推关系,可以矩阵快速幂
最后的答案就是
[
1
,
0...0
]
[1,0...0]
[1,0...0] 乘上转移矩阵的
n
n
n 次方
就是转移矩阵的
n
n
n 次方的第零行和
代码:
#include <bits/stdc++.h>
using namespace std;
#define re register
namespace IO {
char _buf[1 << 21], *_p1 = _buf, *_p2 = _buf;
#define ch() \
(_p1 == _p2 && \
(_p2 = (_p1 = _buf) + fread(_buf, 1, 1 << 21, stdin), _p1 == _p2) \
? EOF \
: *_p1++)
inline int in() {
int s = 0, f = 1;
char x = getchar();
while (x < '0' || x > '9') {
if (x == '-') f = -1;
x = getchar();
}
while (x >= '0' && x <= '9') {
s = (s * 10) + (x & 15);
x = getchar();
}
return f = 1 ? s : -s;
}
char _buf_[1 << 21];
int _p1_ = -1;
inline void flush() {
fwrite(_buf_, 1, _p1_ + 1, stdout);
_p1_ = -1;
}
inline void pc(char x) {
if (_p1_ == (1 << 21) - 1) flush();
_buf_[++_p1_] = x;
}
inline void out(int x) {
char k[20];
int tot = 0;
if (!x) {
pc('0');
return;
}
if (x < 0) {
pc('-');
x = -x;
}
while (x) {
k[++tot] = (x % 10) | 48;
x /= 10;
}
for (int i = tot; i; i--) pc(k[i]);
return;
}
} // namespace IO
using namespace IO;
const int A = 50;
int n, m, mod;
char a[A];
int fail[A];
struct node {
int s[A][A];
inline void clean() {
for (int i = 0; i < m; i++)
for (int j = 0; j < m; j++) s[i][j] = 0;
return;
}
inline friend node operator*(const node u, const node v) {
node r;
r.clean();
for (int i = 0; i < m; i++)
for (int j = 0; j < m; j++)
for (int k = 0; k < m; k++)
r.s[i][k] = (r.s[i][k] + u.s[i][j] * v.s[j][k] % mod) % mod;
return r;
}
} p;
inline void prepare() {
for (int i = 1, j = 0; i <= m; i++) {
while (j && a[i + 1] != a[j + 1]) j = fail[j];
if (a[i + 1] == a[j + 1]) j++;
fail[i + 1] = j;
}
for (int i = 0; i < m; i++) {
for (int j = 0; j <= 9; j++) {
int t = i;
while (t && a[t + 1] != j + '0') t = fail[t];
if (a[t + 1] == j + '0') t++;
p.s[i][t]++;
}
}
return;
}
inline node power(node u, int c) {
node r;
r.clean();
for (int i = 0; i < m; i++) r.s[i][i] = 1;
while (c) {
if (c & 1) r = r * u;
u = u * u;
c >>= 1;
}
return r;
}
signed main() {
n = in(), m = in(), mod = in();
scanf("%s", a + 1);
prepare();
p = power(p, n);
int res = 0;
for (int i = 0; i < m; i++) res = (res + p.s[0][i]) % mod;
out(res), pc('\n');
flush();
return 0;
}
T2 P3715 [BJOI2017]魔法咒语
上面那道题的加强版(加强了不止一个档次吧……
思路:
观察数据范围,分层做
对于前 60pts:
对禁止串建 ACM,对于每一个点预处理出每个串转移后的点
然后 AC自动机 上 dp 即可
对于后 40pts:
先考虑基本词汇长度为 1 的情况
类似上一道题,得出 dp 方程:
f
i
,
j
=
∑
k
=
0
t
o
t
f
i
−
1
,
k
×
g
k
,
j
f_{i,j}=\sum_{k=0}^{tot}f_{i-1,k}\times g_{k,j}
fi,j=k=0∑totfi−1,k×gk,j
于是可以矩阵快速幂优化
考虑毒瘤的长度为 1,2 的情况
对于一组
f
i
f_i
fi 可以由
f
i
−
1
,
f
i
−
2
f_{i-1},f_{i-2}
fi−1,fi−2 转移
发现类似斐波那契数列的转移
类比斐波那契数列的矩阵
∣
f
i
,
0
f
i
,
1
.
.
.
f
i
,
t
o
t
f
i
−
1
,
0
f
i
−
1
,
1
.
.
.
f
i
−
1
,
t
o
t
∣
⇓
∣
f
i
+
1
,
0
f
i
+
1
,
1
.
.
.
f
i
+
1
,
t
o
t
f
i
,
0
f
i
,
1
.
.
.
f
i
,
t
o
t
∣
\left| \begin{matrix} f_{i,0} & f_{i,1} & ... & f_{i,tot} & f_{i-1,0} & f_{i-1,1} & ... & f_{i-1,tot}\\ \end{matrix} \right|\\ \Downarrow\\ \left| \begin{matrix} f_{i+1,0} & f_{i+1,1} & ... & f_{i+1,tot} &f_{i,0} & f_{i,1} & ... & f_{i,tot}\\ \end{matrix} \right|\\
∣∣fi,0fi,1...fi,totfi−1,0fi−1,1...fi−1,tot∣∣⇓∣∣fi+1,0fi+1,1...fi+1,totfi,0fi,1...fi,tot∣∣
建出转移矩阵
手玩理解
然后矩阵快速幂即可
代码:
#include <bits/stdc++.h>
using namespace std;
#define re register
#define int long long
namespace IO {
char _buf[1 << 21], *_p1 = _buf, *_p2 = _buf;
#define ch() \
(_p1 == _p2 && \
(_p2 = (_p1 = _buf) + fread(_buf, 1, 1 << 21, stdin), _p1 == _p2) \
? EOF \
: *_p1++)
inline int in() {
int s = 0, f = 1;
char x = getchar();
while (x < '0' || x > '9') {
if (x == '-') f = -1;
x = getchar();
}
while (x >= '0' && x <= '9') {
s = (s * 10) + (x & 15);
x = getchar();
}
return f = 1 ? s : -s;
}
char _buf_[1 << 21];
int _p1_ = -1;
inline void flush() {
fwrite(_buf_, 1, _p1_ + 1, stdout);
_p1_ = -1;
}
inline void pc(char x) {
if (_p1_ == (1 << 21) - 1) flush();
_buf_[++_p1_] = x;
}
inline void out(int x) {
char k[20];
int tot = 0;
if (!x) {
pc('0');
return;
}
if (x < 0) {
pc('-');
x = -x;
}
while (x) {
k[++tot] = (x % 10) | 48;
x /= 10;
}
for (int i = tot; i; i--) pc(k[i]);
return;
}
} // namespace IO
using namespace IO;
const int A = 210;
const int mod = 1e9 + 7;
int n, m, L;
char a[55][A], b[A];
int s[A];
int tr[A][26], tot;
int tag[A], fail[A];
inline void add() {
int len = strlen(b + 1);
int p = 0;
for (int i = 1; i <= len; i++) {
if (!tr[p][b[i] - 'a']) tr[p][b[i] - 'a'] = ++tot;
p = tr[p][b[i] - 'a'];
}
tag[p] = 1;
return;
}
inline void build() {
queue<int> q;
int p = 0;
for (int i = 0; i < 26; i++) {
p = tr[0][i];
if (!p) continue;
q.push(p);
}
while (!q.empty()) {
int x = q.front();
q.pop();
for (int i = 0; i < 26; i++) {
p = tr[x][i];
if (!p) {
tr[x][i] = tr[fail[x]][i];
continue;
}
q.push(p);
fail[p] = tr[fail[x]][i];
}
if (tag[fail[x]]) tag[x] = 1;
}
return;
}
namespace Sub1 {
int to[A][A], f[A][A];
inline void prepare() {
for (int i = 0; i <= tot; i++)
for (int j = 1; j <= n; j++) {
int p = i, len = strlen(a[j] + 1);
for (int k = 1; k <= len; k++) {
p = tr[p][a[j][k] - 'a'];
if (tag[p]) {
p = -1;
break;
}
}
to[i][j] = p;
}
return;
}
inline void DP() {
f[0][0] = 1;
for (int i = 0; i < L; i++)
for (int j = 0; j <= tot; j++)
for (int k = 1; k <= n; k++)
if (to[j][k] != -1)
f[i + s[k]][to[j][k]] = (f[i + s[k]][to[j][k]] + f[i][j]) % mod;
return;
}
inline void work() {
prepare();
DP();
int res = 0;
for (int i = 0; i <= tot; i++) res = (res + f[L][i]) % mod;
out(res), pc('\n');
flush();
exit(0);
}
} // namespace Sub1
namespace Sub2 {
struct Mat {
int a[A][A];
inline void clean() {
for (int i = 0; i < (tot + 1) * 2; i++)
for (int j = 0; j < (tot + 1) * 2; j++) a[i][j] = 0;
return;
}
inline friend Mat operator*(Mat u, Mat v) {
Mat r;
r.clean();
for (int i = 0; i < (tot + 1) * 2; i++)
for (int j = 0; j < (tot + 1) * 2; j++)
for (int k = 0; k < (tot + 1) * 2; k++)
r.a[i][j] = (r.a[i][j] + u.a[i][k] * v.a[k][j] % mod) % mod;
return r;
}
} mx;
inline void prepare() {
for (int i = 0; i <= tot; i++) {
for (int j = 1; j <= n; j++) {
if (s[j] == 1) {
int p = tr[i][a[j][1] - 'a'];
if (!tag[p]) mx.a[i][p]++;
} else {
int p = tr[i][a[j][1] - 'a'];
if (tag[p]) continue;
p = tr[p][a[j][2] - 'a'];
if (!tag[p]) mx.a[i + tot + 1][p]++;
}
}
}
for (int i = 0; i <= tot; i++) mx.a[i][i + tot + 1] = 1;
return;
}
inline Mat power(Mat s, int c) {
Mat r;
r.clean();
for (int i = 0; i < (tot + 1) * 2; i++) r.a[i][i] = 1;
while (c) {
if (c & 1) r = r * s;
s = s * s;
c >>= 1;
}
return r;
}
inline void work() {
prepare();
mx = power(mx, L);
int res = 0;
for (int i = 0; i <= tot; i++) res = (res + mx.a[0][i]) % mod;
out(res), pc('\n');
flush();
exit(0);
}
} // namespace Sub2
signed main() {
n = in(), m = in(), L = in();
for (int i = 1; i <= n; i++) {
scanf("%s", a[i] + 1);
s[i] = strlen(a[i] + 1);
}
for (int i = 1; i <= m; i++) {
scanf("%s", b + 1);
add();
}
build();
if (L <= 100)
Sub1::work();
else
Sub2::work();
return 0;
}