\(\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. 关于咪咕视频的m3u8再次解析

    软件和源码 前言 之前写过一片文章: 关于突破咪咕视频付费限制的研究, 但是后来我发现评论说已经不能用了,我知道肯定是api修改了,写这种东西就是这样,不一定什么时候就变化了,然后就用不了了,我懒得继 ...

  2. 常用内置模块os sys json

    今日内容回顾 目录 今日内容回顾 os模块 sys模块 json模块 json模块实战 os模块 sys模块 json模块 os模块 os模块主要与代码运行的操作系统打交道 1.创建目录(文件夹) i ...

  3. Burp Suite安装

    1.Burpsuite简介  Burp Suite是一款集成化的渗透测试工具,包含了很多功能,可以帮助我们高效地完成对Web应用程序的渗透测试和攻击. Burp Suite 由Java语言编写,基于J ...

  4. linux挖矿处置

    挖矿的类型 主动挖矿:用户在个人电脑或服务器使用挖矿程序进行CPU,GPU计算,获取虚拟货币. 被动挖矿:挖矿病毒通过系统漏洞,恶意程序,弱口令等方式入侵服务器,设备感染挖矿病毒后会开始挖掘虚拟货币. ...

  5. 比Sqoop功能更加强大开源数据同步工具DataX实战

    @ 目录 概述 定义 与Sqoop对比 框架设计 支持插件 核心架构 核心优势 部署 基础环境 安装 从stream读取数据并打印到控制台 读取MySQL写入HDFS 读取HDFS写入MySQL 执行 ...

  6. (已转)C++知识图谱

  7. Flask初步认识

    1.Flask基本认识 Flask 本身相当于一个内核,其他几乎所有的功能都要用到扩展包(数据库Flask-SQLAlchemy),都需要用第三方的扩展来实现.比如可以用 Flask 扩展加入ORM. ...

  8. 【转载】【WinAPI】LockWindowUpdate的函数的用法

    DelPhi LockWindowUpdate的函数的用法 Application.ProcessMessages; LockWindowUpdate(Self.Handle); //锁住当前窗口 L ...

  9. 学习ASP.NET Core Blazor编程系列十九——文件上传(下)

    学习ASP.NET Core Blazor编程系列文章之目录 学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应 ...

  10. Python网络爬虫get方法出现乱码的解决的三种方案

    给大家祭出网络爬虫过程中三种中文乱码的处理方案,希望对大家的学习有所帮助. 方案一 将requests.get().text改为requests.get().content 我们可以看到通过text( ...