博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
HDU-4534 郑厂长系列故事——新闻净化 AC自动机+DP
阅读量:5963 次
发布时间:2019-06-19

本文共 3847 字,大约阅读时间需要 12 分钟。

题意:给定一些单词,这些单词必须要是一个目标串的子串,同时给定一些串,这些串不能够出现在目标串中,其余一些串只会带来不同附加值。现在问满足前两者的情况下,要求附加值最大。数据给定一个原始串,现在要求在这些串中删除一些字符,输出在满足要求的情况下删除最少的字符并保证附加值尽可能的大。

分析:这题的一个暴力的方法肯定就是对于每个字符枚举删或者不删,然后选择一种方案即可。在这个蛮力法的后面注意到其实在枚举的时候还是有很多重复计算的,比如前a个字符删除或者不删除某个字符对于后面的选择是一样的,而题目要求拥有所有的必有串,因此可以将所有的必有串建立一个ac自动机,然后根据枚举在ac自动机相应节点(状态)的位置从而避免了纯粹的暴力枚举。在匹配的过程中保证不允许非法串被包含进去,设dp[i][j][k]表示处理到第i个字符时,ac自动机中的状态是j,并且包含必有串的状态压缩之后的值为k的最少操作次数和相应的最大的附加值(具体体现在两个三维数组)。那么枚举i-1所有的状态,就能够根据第i个字符在ac自动机中进行转移。

#include 
#include
#include
#include
#include
#include
using namespace std;const int N = 1605;const int inf = 0x3f3f3f3f;int n;int sz;char str[105];int del[2][N][1<<8]; // 记录某一状态下最少的更改次数int sco[2][N][1<<8]; // 在满足最少次数的情况下最大的得分,优先更改次数减少,其次是得分的最大化struct Ac_auto { int ch[N][26]; int fail[N]; int sta[N]; int gain[N]; char kill[N]; // 只取0,1两个值 int idx, root; int newnd() { memset(ch[idx], 0, sizeof (ch[idx])); gain[idx] = sta[idx] = fail[idx] = kill[idx] = 0; return idx++; } void init() { idx = 0, root = newnd(); } void insert(char ss[], int val) { int len = strlen(ss); int p = root; for (int i = 0; i < len; ++i) { char c = ss[i]-'a'; if (!ch[p][c]) ch[p][c] = newnd(); p = ch[p][c]; } // 由于题目中说明了不包括相同的单词,因此只需要等于赋值 if (val == 999) sta[p] = 1 << sz; else if (val == -999) kill[p] = 1; else gain[p] = val; } void build() { // 构建失败指针,ac自动机的精华 queue
q; for (int i = 0; i < 26; ++i) { if (ch[root][i]) { q.push(ch[root][i]); } } while (!q.empty()) { int p = q.front(); q.pop(); for (int i = 0; i < 26; ++i) { int v = ch[p][i]; int x = fail[p]; if (v) { q.push(v); while (x && !ch[x][i]) x = fail[x]; // 选择一个最长的后缀串,使fail指针指向它 fail[v] = ch[x][i]; // 无论有没有指向i的节点,其fail指针会指向0 kill[v] |= kill[fail[v]]; // 如果该字符串之中含有非法的串,那么非法性质会传递过来 sta[v] |= sta[fail[v]]; // 状态也需要传递 gain[v] += gain[fail[v]]; // 传递值 } else { ch[p][i] = ch[x][i]; } } } }};Ac_auto ac;void solve() { memset(del, 0x3f, sizeof (del)); // 对于删除次数初始化为正无穷大 memset(sco, 0x80, sizeof (sco)); // 对于得分初始化为负无穷大 int cur = 0, nxt = 1; del[cur][0][0] = 0; // 初始化没有字符时不包含任何合法和非法的状态的最少更改字符数为0 sco[cur][0][0] = 0; // 配套的获得的价值为0 int len = strlen(str); int lim = 1 << sz; const int &idx = ac.idx; for (int i = 0; i < len; ++i) { // i表示匹配到了那个字符 int c = str[i]-'a'; memset(del[nxt], 0x3f, sizeof (del[nxt])); memset(sco[nxt], 0x80, sizeof (sco[nxt])); for (int j = 0; j < idx; ++j) { // j记录在ac自动机中匹配到的位置 for (int k = 0; k < lim; ++k) { // k表示压缩后的包含必须子串的情况 if (del[cur][j][k] == inf) continue; // 如果这个状态没有计算过,其也就无法转移到新的状态 // 如果删除该字符 if (del[nxt][j][k] > del[cur][j][k] + 1) { del[nxt][j][k] = del[cur][j][k] + 1; sco[nxt][j][k] = sco[cur][j][k]; } else if (del[nxt][j][k] == del[cur][j][k] + 1) { if (sco[nxt][j][k] < sco[cur][j][k]) { sco[nxt][j][k] = sco[cur][j][k]; } } // 如果匹配该字符 int np = ac.ch[j][c]; if (ac.kill[np]) continue; // 如果不能够匹配或者该子串不允许被包含 int gain = ac.gain[np]; int nsta = k|ac.sta[np]; if (del[nxt][np][nsta] > del[cur][j][k]) { del[nxt][np][nsta] = del[cur][j][k]; sco[nxt][np][nsta] = sco[cur][j][k] + gain; } else if (del[nxt][np][nsta] == del[cur][j][k]) { if (sco[nxt][np][nsta] < sco[cur][j][k] + gain) { sco[nxt][np][nsta] = sco[cur][j][k] + gain; } } } } swap(cur, nxt); } int xdel = inf, xsco; for (int i = 0; i < idx; ++i) { if (xdel > del[cur][i][lim-1]) { xdel = del[cur][i][lim-1]; xsco = sco[cur][i][lim-1]; } else if (xdel == del[cur][i][lim-1]) { xsco = max(xsco, sco[cur][i][lim-1]); } } if (xdel == inf) { puts("Banned"); } else { printf("%d %d\n", xdel, xsco); }}int main() { int T, ca = 0; scanf("%d", &T); while (T--) { int val; sz = 0; ac.init(); scanf("%d", &n); for (int i = 0; i < n; ++i) { scanf("%s %d", str, &val); ac.insert(str, val); if (val == 999) ++sz; } ac.build(); scanf("%s", str); printf("Case %d: ", ++ca); solve(); } return 0;}

 

转载于:https://www.cnblogs.com/Lyush/p/3422254.html

你可能感兴趣的文章
Python内置容器(2)——字典,迭代器,列表解析
查看>>
那年匆匆 -大学
查看>>
Internet 打印提示“打印机安装失败、打印机名称无效”的解决
查看>>
从Powershell ***脚本学到的如何执行后台runspace~
查看>>
SCCM TP4部署Office2013
查看>>
Linux系统启动过程,grub重装。
查看>>
使用Putty密钥认证机制远程登录Linux
查看>>
一不小心,老司机又翻车了
查看>>
理解思科IPS系统的traffic flow notifications
查看>>
【博客话题】技术人生之三界修炼
查看>>
Ext JS 6开发实例(三) :主界面设计
查看>>
Hyper-V 3中虚拟机CPU竞争机制
查看>>
【原创】Oracle RAC原理和安装
查看>>
东哥读书小记 之 《MacTalk人生元编程》
查看>>
《随机出题软件》&《随机分队软件》源码(Windows API)
查看>>
python 文件及文件夹操作
查看>>
Android自定义ListView的Item无法响应OnItemClick的解决办法
查看>>
Building Apps for Windows Phone 8.1教程下载地址整理
查看>>
移动Web—CSS为Retina屏幕替换更高质量的图片
查看>>
[Linux 性能检测工具]SAR
查看>>