设计密码
你现在需要设计一个密码
S
,
S
S,S
S,S 需要满足:
S 的长度是 N;
S 只包含小写英文字母;
S 不包含子串 T;
例如: a b c abc abc 和 a b c d e abcde abcde 是 a b c d e abcde abcde 的子串, a b d abd abd 不是 a b c d e abcde abcde 的子串。
请问共有多少种不同的密码满足要求?
(由于答案会非常大,请输出答案模 1 e 9 + 7 1e9+7 1e9+7 的余数。)
输入格式
第一行输入整数
N
N
N,表示密码的长度。
第二行输入字符串 T , T T,T T,T中只包含小写字母。
输出格式
输出一个正整数,表示总方案数模
1
e
9
+
7
1e9+7
1e9+7 后的结果。
数据范围
1
≤
N
≤
50
1≤N≤50
1≤N≤50,
1
≤
∣
T
∣
≤
N
1≤|T|≤N
1≤∣T∣≤N,
∣
T
∣
|T|
∣T∣是
T
T
T的长度。
本题是DP与KMP的结合,具体状态设计是:
f
[
i
]
[
j
]
:
构
造
了
前
i
个
字
母
,
且
当
前
字
母
对
串
T
的
状
态
是
j
,
(
这
个
状
态
指
的
是
T
的
K
M
P
的
n
e
x
t
数
组
)
f[i][j]:构造了前i个字母,且当前字母对串T的状态是j,\\(这个状态指的是T的KMP的next数组)
f[i][j]:构造了前i个字母,且当前字母对串T的状态是j,(这个状态指的是T的KMP的next数组)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 55, mod = 1e9 + 7;
int n, m;
int f[N][N];
//f[i][j]表示前i个字母且对于串T的状态是j的所有方案
char T[N];
int ne[N];
int main()
{
cin >> n >> T + 1;
m = strlen(T + 1);
//求T的next数组
for(int i = 2, j = 0; i <= m; i++)
{
while(j && T[i] != T[j + 1]) j = ne[j];
if(T[i] == T[j + 1]) j++;
ne[i] = j;
}
f[0][0] = 1; //长度为0时状态为0只有一种方案
for(int i = 0; i <= n; i++) //枚举长度
for(int j = 0; j <= m; j++)//枚举状态
for(char k = 'a'; k <= 'z'; k++) //枚举下一位的字母
{
int u = j;
while(u && k != T[u + 1]) u = ne[u];
//求在next数组中转移后的状态
if(k == T[u + 1]) u++;
if(u < m) f[i + 1][u] = (f[i + 1][u] + f[i][j]) % mod;
//如果状态合法才进行dp的转移,合法指当前匹配的字母个数u小于m
}
int res = 0;
for(int i = 0; i < m; i++) res = (res + f[n][i]) % mod;
cout << res << endl;
return 0;
}
文本生成器
题目描述
JSOI 交给队员 ZYX 一个任务,编制一个称之为“文本生成器”的电脑软件:该软件的使用者是一些低幼人群,他们现在使用的是 GW 文本生成器 v6 版。
该软件可以随机生成一些文章——总是生成一篇长度固定且完全随机的文章。 也就是说,生成的文章中每个字符都是完全随机的。如果一篇文章中至少包含使用者们了解的一个单词,那么我们说这篇文章是可读的(我们称文章 s s s 包含单词 t t t,当且仅当单词 t t t 是文章 s s s 的子串)。但是,即使按照这样的标准,使用者现在使用的 GW 文本生成器 v6 版所生成的文章也是几乎完全不可读的。ZYX 需要指出 GW 文本生成器 v6 生成的所有文本中,可读文本的数量,以便能够成功获得 v7 更新版。你能帮助他吗?
答案对 1 0 4 + 7 10^4 + 7 104+7 取模。
输入格式
第一行有两个整数,分别表示使用者了解的单词总数
n
n
n 和生成的文章长度
m
m
m。
接下来
n
n
n 行,每行一个字符串
s
i
s_i
si ,表示一个使用者了解的单词。
输出格式
输出一行一个整数表示答案对
1
0
4
+
7
10^4 + 7
104+7 取模的结果。
上一题是构造长度为
n
n
n的且不包含给定子串的字符串的个数。
本题是构造长度为
n
n
n的且不包含多个给定子串的字符串的个数
本题是上一题的AC自动机版本,状态设计也和上一题类似:
f
[
i
]
[
j
]
:
长
度
是
i
,
在
a
c
自
动
机
上
的
状
态
是
j
的
所
有
不
包
含
给
定
单
词
的
字
符
串
的
个
数
f[i][j]:长度是i,在ac自动机上的状态是j的所有不包含给定单词的字符串的个数
f[i][j]:长度是i,在ac自动机上的状态是j的所有不包含给定单词的字符串的个数
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
const int N = 110, M = 6010, Mod = 10007;
int n, m, f[N][M]; //f[i][j]:长度是i,在ac自动机上的状态是j的所有不包含给定单词的字符串的个数
int idx, tr[M][26], ne[M];
bool have[M];
char str[N];
void insert()
{
int p = 0;
for(int i = 0; str[i]; i ++)
{
int t = str[i] - 'A';
if(!tr[p][t]) tr[p][t] = ++ idx;
p = tr[p][t];
}
have[p] = true;
}
int q[M];
void build()
{
int hh = 0, tt = -1;
for(int i = 0; i < 26; i ++)
if(tr[0][i]) q[++ tt] = tr[0][i];
while(hh <= tt)
{
int u = q[hh ++];
for(int i = 0; i < 26; i ++)
{
int &p = tr[u][i];
if(!p) p = tr[ne[u]][i];
else
{
ne[p] = tr[ne[u]][i];
q[++ tt] = p;
have[p] |= have[ne[p]];
}
}
}
}
int main()
{
#ifdef LOCAL
freopen("in.in", "r", stdin);
freopen("out.out", "w", stdout);
#endif
cin >> n >> m;
for(int i = 0; i < n; i ++)
{
cin >> str;
insert();
}
build();
f[0][0] = 1;
for(int i = 0; i < m; i ++) //枚举长度
for(int j = 0; j <= idx; j ++) //枚举状态
for(int k = 0; k < 26; k ++) //枚举下一位填什么
{
int p = tr[j][k];
if(!have[p]) f[i + 1][p] = (f[i + 1][p] + f[i][j]) % Mod;
//如果状态合法才进行dp的转移,合法指没有字符串在p处结尾
}
int ans = 1;
for(int i = 0; i < m; i ++) ans = (ans * 26) % Mod;
for(int i = 0; i <= idx; i ++) ans = (ans - f[m][i] + Mod) % Mod;
cout << ans << endl;
return 0;
}
u p d a t e , 2021.10.2 update,2021.10.2 update,2021.10.2
最近在学矩阵快速幂,然后惊奇的发现KMP和AC自动机的DP还可以用矩阵快速幂加速。
第一题:GT考试
该题给出一个字符串,问所有长度为
n
n
n的字符串中,有多少个是不包含该字符串的。注意
n
n
n的范围高达
1
0
9
10^9
109!!!
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
const int N = 20;
int n, m, mod;
char str[N];
int ne[N], base[N][N], a[N][N];
void mul(int a[][N], int b[][N]) //矩阵乘法
{
static int c[N][N];
memset(c, 0, sizeof c);
for(int i = 0; i < m; i ++)
for(int j = 0; j < m; j ++)
for(int k = 0; k < m; k ++)
c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % mod;
memcpy(a, c, sizeof c);
}
int qmi(int k) //矩阵快速幂
{
for(int i = 0; i < m; i ++) //求出来base矩阵
for(char ch = '0'; ch <= '9'; ch ++)
{
int j = i;
while(j && str[j + 1] != ch) j = ne[j];
if(str[j + 1] == ch) j ++;
if(j < m) base[i][j] ++;
}
a[0][0] = 1; //答案矩阵,初始化
while(k)
{
if(k & 1) mul(a, base);
mul(base, base);
k >>= 1;
}
int res = 0;//统计结果
for(int i = 0; i < m; i ++) res = (res + a[0][i]) % mod;
return res;
}
int main()
{
#ifdef LOCAL
freopen("in.in", "r", stdin);
freopen("out.out", "w", stdout);
#endif
cin >> n >> m >> mod >> str + 1;
for(int i = 2, j = 0; i <= m; i ++) //KMP
{
while(j && str[j + 1] != str[i]) j = ne[j];
if(str[j + 1] == str[i]) j ++;
ne[i] = j;
}
cout << qmi(n) << endl;
return 0;
}
第二题:POJ2778 DNA Sequence
题目大意:
给定
m
m
m和
n
n
n,分别代表接下来会给你
m
m
m个病毒串(每个串的长度不会超过
10
10
10),问你长度为n的不包含病毒的串有多少种。
(
0
<
=
m
<
=
10
,
1
<
=
n
<
=
2
e
9
)
(0<=m<=10,1<=n<=2e9)
(0<=m<=10,1<=n<=2e9)
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
const int N = 10, M = 110, MOD = 100000;
int n, m;
int idx, tr[M][4], ne[M], q[M];
bool have[M];
char str[N];
int get(char ch)
{
if(ch == 'A') return 0;
if(ch == 'T') return 1;
if(ch == 'G') return 2;
return 3;
}
void insert()
{
int p = 0;
for(int i = 0; str[i]; i ++)
{
int u = get(str[i]);
if(!tr[p][u]) tr[p][u] = ++ idx;
p = tr[p][u];
}
have[p] = true;
}
void build()
{
int hh = 0, tt = -1;
for(int i = 0; i < 4; i ++)
if(tr[0][i]) q[++ tt] = tr[0][i];
while(hh <= tt)
{
int u = q[hh ++];
for(int i = 0; i < 4; i ++)
{
int &p = tr[u][i];
if(!p) p = tr[ne[u]][i];
else
{
ne[p] = tr[ne[u]][i];
q[++ tt] = p;
have[p] |= have[ne[p]];
}
}
}
}
void mul(int a[][M], int b[][M]) //矩阵乘法
{
static int c[M][M];
memset(c, 0, sizeof c);
for(int i = 0; i <= idx; i ++)
for(int j = 0; j <= idx; j ++)
for(int k = 0; k <= idx; k ++)
c[i][j] = (c[i][j] + (LL)a[i][k] * b[k][j]) % MOD;
memcpy(a, c, sizeof c);
}
int base[M][M], a[M][M];
int qmi(int k) //矩阵快速幂
{
for(int i = 0; i <= idx; i ++) //求base矩阵
for(int k = 0; k < 4; k ++)
{
int j = tr[i][k];
if(!have[i] && !have[j]) base[i][j] ++;
//只有当i点和j点都是安全节点时才可以转移
}
//for(int i = 0; i <= idx; i ++) a[i][i] = 1;
a[0][0] = 1; //答案矩阵初始化
while(k)
{
if(k & 1) mul(a, base);
mul(base, base);
k >>= 1;
}
int res = 0; //统计结果
for(int i = 0; i <= idx; i ++) res = (res + a[0][i]) % MOD;
return res;
}
int main()
{
#ifdef LOCAL
freopen("in.in", "r", stdin);
freopen("out.out", "w", stdout);
#endif
cin >> m >> n;
for(int i = 0; i < m; i ++)
{
cin >> str;
insert();
}
build();
//以上是构建AC自动机的过程
cout << qmi(n) << endl;
return 0;
}