\(\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. day29 jQuery选择器 & jquery属性操作 & jquery DOM元素 操作与遍历

    简介 jQuery,顾名思义,就是javascript和query(查询),即辅助javascript开发的库,本质就是一个js文件: jQuery是一个js函数库,是目前全球范围内最流行.用的最多的 ...

  2. 【sqoop】简介、原理、安装配置测试、导入导出案例、脚本打包、常见命令及参数介绍、常用命令举例

    一.sqoop简介 用于在Hadoop(Hive)与传统的数据库(mysql.oracle...)之间进行数据的传递,可以将一个关系型数据库(例如 : MySQL ,Oracle ,Postgres等 ...

  3. vue3 watch笔记

    watchEffect 执行传入的一个函数,同时自动追踪函数中依赖到的数据,并在其依赖变更时重新运行该函数. 并且会在 组件挂载前 立即调用一次,(默认是挂载前,可通过修改 flush 属性改变,后边 ...

  4. CTF隐写术总结

    CTF隐写术总结 1.查看图像属性及详细信息 或者查看图像的高度,利用winhex等工具改变图像宽度或高度,查看是否有隐藏信息. 2.利用winhex或nodepad++打开搜索ctf,flag,ke ...

  5. 前段知识之CSS

    目录 CSS层叠样式表 CSS语法结构: CSS注释语法 引入css的多种方式 CSS选择器 1. CSS基本选择器 2. CSS组合选择器 3. 分组与嵌套 4. 属性选择器 5. 伪类选择器 6. ...

  6. 【转载】EXCEL VBA 选取非连续的单元格区域——Areas集合

    出处:http://www.360doc.com/content/21/1113/17/77710807_1004011085.shtml 前面我们讲的大多是**并操作单个的单元格,或者是连续的单元格 ...

  7. 记录Typescript的学习调试笔记(比 javascript更具面向对象,强类型检查,静态字段,适合现代的大团队分工与管理风格).

    1.)先来一段Typescript的环境安装. 安装nodejs ,下载地址:https://nodejs.org/en/download/                 //(node-v12.1 ...

  8. .Net引用根目录子文件夹下的dll文件

    在.Net开发的时候,有时候会引用一套库,这些库是由多个dll文件.正常情况下,这些dll文件需要拷贝到运行根目录下.如果这些dll文件比较多,加上其他直接引用的dll,这样会导致根目录下非常乱.我们 ...

  9. 数据分析中的SQL如何解决业务问题

    本文来自知乎问答. 提问:数据分析人员需要掌握sql到什么程度? 请问做一名数据分析人员,在sql方面需要掌握到什么程度呢?会增删改查就可以了吗?还是说关于开发的内容也要会?不同阶段会有不同的要求吗? ...

  10. 诗词API

    1.js依赖 /** * 今日诗词V2 JS-SDK 1.2.2 * 今日诗词API 是一个可以免费调用的诗词接口:https://www.jinrishici.com */ !function(e) ...