\(\text{Analysis}\)

做了几道题后发现挺套路的

涉及统计或构造文本串与众多模式串匹配产生贡献或存在限制时的 \(DP\)

一般设 \(f[i][j]\) 表示考虑到文本串第 \(i\) 位,在自动机上的 \(j\) 位置

加上 \(j\) 这一维,把自动机的弄进 \(DP\) 里,方便处理与匹配相关的限制

下面是几道题(按我的做题顺序

\(\text{[JSOI2007]}\) 文本生成器

考虑容斥,所有的方案减去一个模式串都不能出现的方案

\(DP\) 计算一个模式串都不能出现的方案,即在自动机上不能走到模式串结尾的结点

设 \(f[i][j]\) 表示考虑到文本串第 \(i\) 位,走到自动机 \(j\) 位置时的答案

那么枚举当前位填的字母,考虑能不能填

能不能填的判断方法,就是填了之后的自动机的位置,它以及跳 \(fail\) 后遇到的结点均不能是模式串结尾的结点

这个很好理解

然后就没了

$\text{Code}$
#include <cstdio>
#include <cstring>
#define IN inline
using namespace std; const int P = 10007, N = 6005;
int n, m, size = 1;
int tr[N][26], vis[N], Q[N], fail[N], f[105][N];
char s[105]; IN void insert() {
int len = strlen(s), u = 1, c;
for(int i = 0; i < len; i++) {
c = s[i] - 'A';
if (!tr[u][c]) tr[u][c] = ++size;
u = tr[u][c];
}
vis[u] = 1;
} IN void build() {
int h = 0, t = 0;
for(int i = 0; i < 26; i++)
if (tr[1][i]) fail[Q[++t] = tr[1][i]] = 1; else tr[1][i] = 1;
while (h < t) {
int now = Q[++h];
for(int i = 0; i < 26; i++)
if (tr[now][i]) vis[tr[now][i]] |= vis[fail[tr[now][i]] = tr[fail[now]][i]], Q[++t] = tr[now][i];
else tr[now][i] = tr[fail[now]][i];
}
} IN void Add(int &x, int y) {if ((x += y - P) < 0) x += P;}
IN void Dec(int &x, int y) {if ((x -= y) < 0) x += P;} int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%s", s), insert();
build(), f[0][1] = 1;
for(int i = 0; i < m; i++)
for(int j = 1; j <= size; j++)
for(int k = 0; k < 26; k++)
if (!vis[tr[j][k]])
Add(f[i + 1][tr[j][k]], f[i][j]);
int ans = 1;
for(int i = 1; i <= m; i++) ans = 26LL * ans % P;
for(int i = 1; i <= size; i++) Dec(ans, f[m][i]);
printf("%d\n", ans);
}

\(\text{[SDOI2014]}\) 数数

尝试数位 \(DP\),发现不行

关于不能出现匹配,弄进 \(AC\) 自动机里就好了

具体设 \(f[i][j][l][k]\) 表示到了第 \(i\) 位,自动机 \(j\) 的位置上,是否有高位标记,是否有前导零

转移很显然

关于模式串的前导零,在自动机上走时,如果有前导零,那么接下来若填 \(0\),则自动机上的状态应还处于根处

因为压根就没开始。

不填 \(0\) 的话,应从根出发走到相应的儿子结点

所以我改变了下枚举顺序

不过这题数据挺垃圾的

一些奇妙的甚至错误的处理方式都能 \(AC\)

$\text{Code}$
#include <cstdio>
#include <cstring>
#define IN inline
using namespace std; const int N = 1505, P = 1e9 + 7;
int m, size, tr[N][11], fail[N], Q[N], tag[N], f[N][N][2][2], a[N];
char n[N], s[N]; IN void insert() {
int len = strlen(s), u = 0, c;
for(int i = 0; i < len; i++) {
c = s[i] - '0';
if (!tr[u][c]) tr[u][c] = ++size;
u = tr[u][c];
}
tag[u] = 1;
} IN void build() {
int h = 0, t = 0, now;
for(int i = 0; i < 10; i++) if (tr[0][i]) Q[++t] = tr[0][i];
while (h < t) {
now = Q[++h];
for(int i = 0; i < 10; i++)
if (tr[now][i]) tag[tr[now][i]] |= tag[fail[tr[now][i]] = tr[fail[now]][i]], Q[++t] = tr[now][i];
else tr[now][i] = tr[fail[now]][i];
}
} IN void Add(int &x, int y) {if ((x += y - P) < 0) x += P;}
int main() {
scanf("%s%d", n + 1, &m);
for(int i = 1; i <= m; i++) scanf("%s", s), insert();
build(), f[0][0][1][1] = 1;
int len = strlen(n + 1);
for(int i = 1; i <= len; i++) a[i] = n[i] - '0';
for(int i = 0; i < len; i++)
for(int l = 0; l < 2; l++)
for(int k = 0; k <= (l ? a[i + 1] : 9); k++) {
if (!tag[tr[0][k]]) Add(f[i + 1][k ? tr[0][k] : 0][l & (k == a[i + 1])][!k], f[i][0][l][1]);
for(int j = 0; j <= size; j++)
if (!tag[tr[j][k]]) Add(f[i + 1][tr[j][k]][l & (k == a[i + 1])][0], f[i][j][l][0]);
}
int ans = 0;
for(int i = 0; i <= size; i++)
for(int j = 0; j < 2; j++)
for(int k = 0; k < 2; k++) Add(ans, f[len][i][j][k]);
printf("%d\n", (ans - 1 + P) % P);
}

\(\text{[USACO12JAN]Video Game G}\)

这个题就一样一样的了

要初始化

$\text{Code}$
#include <cstdio>
#include <iostream>
#include <cstring>
#define IN inline
using namespace std; const int N = 305, INF = 0x3f3f3f3f;
int n, m, size;
int tag[N], tr[N][3], fail[N], Q[N], f[N << 2][N];
char s[21]; IN void insert() {
int len = strlen(s), u = 0, c;
for(int i = 0; i < len; i++) {
c = s[i] - 'A';
if (!tr[u][c]) tr[u][c] = ++size;
u = tr[u][c];
}
tag[u] = 1;
} IN void build() {
int h = 0, t = 0, now;
for(int i = 0; i < 3; i++) if (tr[0][i]) Q[++t] = tr[0][i];
while (h < t) {
now = Q[++h];
for(int i = 0; i < 3; i++)
if (tr[now][i]) tag[Q[++t] = tr[now][i]] += tag[fail[tr[now][i]] = tr[fail[now]][i]];
else tr[now][i] = tr[fail[now]][i];
}
} int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%s", s), insert();
memset(f, -INF, sizeof f);
build(), f[0][0] = 0;
for(int i = 0; i < m; i++)
for(int j = 0; j <= size; j++)
for(int k = 0; k < 3; k++)
f[i + 1][tr[j][k]] = max(f[i + 1][tr[j][k]], f[i][j] + tag[tr[j][k]]);
int ans = 0;
for(int i = 0; i <= size; i++) ans = max(ans, f[m][i]);
printf("%d\n", ans);
}

\(\text{CF808G Anthem of Berland}\)

好久没写 \(AC\) 自动机上 \(dp\) 了

然而这道题是 \(KMP\) 自动机,实际上不过是只有一个串的 \(AC\) 自动机

温习了一下写法,注意 \(fail\) 初始化!!!

根的儿子结点的 \(fail\) 要先初始化为根本身

$\text{Code}$
#include <bits/stdc++.h>
using namespace std; const int N = 1e5 + 5;
int tr[N][26], fail[N];
char s1[N], s2[N]; int main() {
scanf("%s%s", s1 + 1, s2 + 1);
int len1 = strlen(s1 + 1), len2 = strlen(s2 + 1);
for(int i = 1; i <= len2; i++) tr[i - 1][s2[i] - 'a'] = i;
for(int i = 1; i <= len2; i++)
for(int j = 0; j < 26; j++)
if (tr[i][j]) fail[tr[i][j]] = tr[fail[i]][j];
else tr[i][j] = tr[fail[i]][j];
vector<vector<int>> f(len1 + 1, vector<int>(len2 + 1, -1e9));
f[0][0] = 0;
for(int i = 0; i < len1; i++) {
for(int j = 0; j <= len2; j++) if (f[i][j] != -1e9) {
if (s1[i + 1] != '?') {
int ch = tr[j][s1[i + 1] - 'a'];
f[i + 1][ch] = max(f[i + 1][ch], f[i][j] + (ch == len2));
continue;
}
for(int k = 0; k < 26; k++)
f[i + 1][tr[j][k]] = max(f[i + 1][tr[j][k]], f[i][j] + (tr[j][k] == len2));
}
}
int ans = -1e9;
for(int i = 0; i <= len2; i++) ans = max(ans, f[len1][i]);
printf("%d\n", ans);
}

AC 自动机上 DP的更多相关文章

  1. 【洛谷4045】[JSOI2009] 密码(状压+AC自动机上DP)

    点此看题面 大致题意: 给你\(n\)个字符串,问你有多少个长度为\(L\)的字符串,使得这些字符串都是它的子串.若个数不大于\(42\),按字典序输出所有方案. 状压 显然,由于\(n\)很小,我们 ...

  2. Passwords Gym - 101174E (AC自动机上DP)

    Problem E: Passwords \[ Time Limit: 1 s \quad Memory Limit: 256 MiB \] 题意 给出两个正整数\(A,B\),再给出\(n\)个字符 ...

  3. bzoj [Sdoi2014]数数 AC自动机上dp

    [Sdoi2014]数数 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 1264  Solved: 636[Submit][Status][Discu ...

  4. 【Luogu】P3311数数(AC自动机上DP)

    题目链接 蒟蒻今天终于学会了AC自动机,感觉很稳 (后一句愚人节快乐) 这题开一个f[i][j][k]表示有没有受到限制,正在枚举第j位,来到了AC自动机的第k个节点 的方案数 随后可以刷表更新 注意 ...

  5. URAL 1158 AC自动机上的简单DP+大数

    题目大意 在一种语言中的字母表中有N(N<=50)个字母,每个单词都由M(M<=50)个字母构成,因此,一共可以形成N^M个单词.但是有P(P<=10)个串是被禁止的,也就是说,任何 ...

  6. bzoj4032/luoguP4112 [HEOI2015]最短不公共子串(后缀自动机+序列自动机上dp)

    bzoj4032/luoguP4112 [HEOI2015]最短不公共子串(后缀自动机+序列自动机上dp) bzoj Luogu 题解时间 给两个小写字母串 $ A $ , $ B $ ,请你计算: ...

  7. POJ 3691 AC自动机上的dp

    题目大意: 给定一些不合理的DNA序列,再给一段较长的dna序列,问最少修改几次可以使序列中不存在任何不合理序列,不能找到修改方法输出-1 这里你修改某一个点的DNA可能会影响后面,我们不能单纯的找匹 ...

  8. HNU 13108-Just Another Knapsack Problem (ac自动机上的dp)

    题意: 给你一个母串,多个模式串及其价值,求用模式串拼接成母串(不重叠不遗漏),能获得的最大价值. 分析: ac自动机中,在字典树上查找时,用dp,dp[i]拼成母串以i为结尾的子串,获得的最大价值, ...

  9. 【BZOJ1030】[JSOI2007] 文本生成器(AC自动机上跑DP)

    点此看题面 大致题意: 给你\(N\)个字符串(只含大写字母),要你求出有多少个由\(M\)个大写字母构成的字符串含有这\(N\)个字符串中的至少一个. \(AC\)自动机 看到题目,应该比较容易想到 ...

  10. BZOJ 1030 文本生成器 | 在AC自动机上跑DP

    题目: http://www.lydsy.com/JudgeOnline/problem.php?id=1030 题解: 鸽 #include<cstdio> #include<al ...

随机推荐

  1. python3小技巧总结(实时更新)

    1.列表解析 如果一个想将一个列表中的大于0的数字过滤,一般可能会用到lambd结合filter,或者就是直接遍历,不过最好的解决办法是这样: b = [1,0,-1,-2] a = [i for i ...

  2. 前端工程化与webpack的介绍

    前端工程化 概念:在企业级的前端项目开发中,把前端开发所需的工具.技术.流程.经验等进行规范化.标准化. 模块化 js的模块化,css的模块化,资源的模块化 组件化 复用现有的UI结构,样式,行为 规 ...

  3. day28-jQuery01

    jQuery01 参考文档1:jQuery API 中文文档 | jQuery API 中文在线手册 | jquery api 下载 | jquery api chm (cuishifeng.cn) ...

  4. 【极客时间】大数据概述及HDFS介绍

  5. 【SQL查询】必会的常用函数:条件函数、日期函数、文本函数、窗口函数

    〇.概述 1.内容 选择(双分支.多分支) 一.条件函数 1.计算25岁以上和以下的用户数量 CASE THEN END多分支选择 SELECT (CASE WHEN age>=25 THEN ...

  6. 【每日一题】2022年2月10日-NC160 二分查找-I

    描述请实现无重复数字的升序数组的二分查找 给定一个 元素升序的.无重复数字的整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标(下标 ...

  7. 3.7V升压5V,3.7V转5V电路图芯片

    锂离子电池在如今是广泛应用存在我们生活中的方方面面的电子产品中.如,电子玩具,美容仪,医疗产品,智能手表,手机,笔记本,电动汽车等等非常多. 锂电池3.7V升压到5V,3.7V转5V稳压输出的电子产品 ...

  8. [seaborn] seaborn学习笔记0-seaborn学习笔记章节

    seaborn学习笔记章节 seaborn是一个基于matplotlib的Python数据可视化库.seaborn是matplotlib的高级封装,可以绘制有吸引力且信息丰富的统计图形.相对于matp ...

  9. Siri Shortcut

      AppDelegate.m         //#pragma mark - INUIAddVoiceShortcutButtonDelegate //新添加 - (void)presentAdd ...

  10. P1162填涂颜色——题解

    题目描述 由数字0组成的方阵中,有一任意形状闭合圈,闭合圈由数字1构成,围圈时只走上下左右4个方向.现要求把闭合圈内的所有空间都填写成2.例如:6×6的方阵(n=6),涂色前和涂色后的方阵如下: 0 ...