POJ1699【AC自动机+状压DP_感言】
萌新感言:
我的天呐!
因为是AC自动机的专题所以没有管别的。。。硬着头皮吃那份题解(代码)。。【请戳简单美丽可爱的代码(没开玩笑)】
首先讲AC自动机:
tag存的是以这个节点为后缀的字符串个数(已状压)。
我们在构造fail指针的时候,采用的是BFS的手段,对于树而言,那就是一层一层的向下遍历,
所以代码很巧妙(不能说巧妙吧,就是这样的),在找到fail位置的时候直接跳出,然后更新了tag,为什么可以这样?因为长的找到的时候,短的早就找过了,所以直接更新就好了。
然后这份代码有一个小瑕疵(讲错请吐槽!):因为在构造fail指针的时候tag已经更新过了,所以在DP的时候没必要再找到fail指针然后更新。
自身问题(可跳过):
还有构造fail的函数中当节点不存在的时候,这个节点的位置,用父节点的这个元素的fail指针取代了,如图:
道理还是一样,我要保证长的找到的时候,短的早就找过了。
然后讲DP:
感觉AC自动机下的DP很好理解,因为Trie树上本身对于每个节点就是一种种状态,用BFS的手段从上层到下层遍历。
DP[ i ][ j ]表示匹配到 i 节点,匹配到 j 个字符串时的最短步数。
每次只会伸展一个新节点,从而获取更多的后缀串,所以
dp[x][new_string_num]=min(dp[x][new_string_num],dp[x的父节点][old_string_num]+1);
that's all,thanks for watching....
//#include <bits/stdc++.h>
#include<iostream>
#include<queue>
#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
typedef pair<int,int> PII;
const int N=205;
const int INF=0x3f3f3f3f;
int n,dp[N][1030];
int g[N][4],fail[N],tag[N];
int sz; void init()
{
sz=1;
tag[0]=0;
memset(g[0],0,sizeof(g[0]));
} int GetID(char x)
{
if(x=='A') return 0;
if(x=='T') return 1;
if(x=='C') return 2;
return 3;
} void INS(char *str,int id)
{
int len=strlen(str),index,root=0;
for(int i=0;i<len;i++){
index=GetID(str[i]);
if(g[root][index]==0)
{
tag[sz]=0;
memset(g[sz],0,sizeof(g[sz]));
g[root][index]=sz++;
}
root=g[root][index];
}
tag[root]|=(1<<id);
} void Build_fail()
{
queue<int>que;
for(int i=0;i<4;i++)
{
int u=g[0][i];
if(u){
fail[u]=0;
que.push(u);
}
} while(!que.empty())
{
int root=que.front();
que.pop();
for(int i=0;i<4;i++){
int u=g[root][i];
if(!u){
g[root][i]=g[fail[root]][i];//如果这个节点不存在 用父节点的这个元素的fail指针取代了
continue;
}
que.push(u);
int v=fail[root];
while(v && g[v][i]==0)
v=fail[v];
fail[u]=g[v][i]; //构造
tag[u]|=tag[fail[u]]; //更新节点存的字符串个数。
}
}
} int solve()
{
//初始化
Build_fail();
memset(dp,INF,sizeof(dp));
dp[0][0]=0;
queue<PII>que;
que.push(make_pair(0,0));//塞入根节点,匹配0; while(!que.empty())
{
int u=que.front().first;
int s=que.front().second;
que.pop(); for(int i=0;i<4;i++){
int k=g[u][i]; //匹配这个节点,因为之前当节点不存在的时候已经存了父节点的该元素的fail指针,所以不用考虑为空
int ss=s|tag[k]; //在建立fail指针的时候,tag[k]存的字符串个数已经更新
if(dp[k][ss]>dp[u][s]+1)
{
dp[k][ss]=dp[u][s]+1;
que.push(make_pair(k,ss));
if(ss==(1<<n)-1)
return dp[k][ss];
}
}
}
return 0;
} int main()
{
int T;
char s[30];
scanf("%d",&T);
while(T--){
init();
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%s",s);
INS(s,i);
}
printf("%d\n",solve());
}
return 0;
}
POJ1699【AC自动机+状压DP_感言】的更多相关文章
- hdu 2825 aC自动机+状压dp
Wireless Password Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others ...
- hdu 6086 -- Rikka with String(AC自动机 + 状压DP)
题目链接 Problem Description As we know, Rikka is poor at math. Yuta is worrying about this situation, s ...
- [BZOJ1559]密码 AC自动机+状压
问题 K: [JSOI2009]密码 时间限制: 1 Sec 内存限制: 64 MB 题目描述 众所周知,密码在信息领域起到了不可估量的作用.对于普通的登陆口令,唯一的破解 方法就是暴力破解一逐个尝 ...
- bzoj 1212: [HNOI2004]L语言 AC自动机+状压
为什么这道题网上所有题解写的都是N*Len的trie树的暴力啊,4E的复杂度... 为什么暴力还跑这么快啊TAT.. 有一个O(Len)的做法就是先把AC自动机建出来,因为每个字典串的长度很小,所以我 ...
- [HNOI2006]最短母串问题——AC自动机+状压+bfs环形处理
Description 给定n个字符串(S1,S2,„,Sn),要求找到一个最短的字符串T,使得这n个字符串(S1,S2,„,Sn)都是T的子串. 32MB Input 第一行是一个正整数n(n< ...
- BZOJ1559 [JSOI2009]密码 【AC自动机 + 状压dp】
题目链接 BZOJ1559 题解 考虑到这是一个包含子串的问题,而且子串非常少,我们考虑\(AC\)自动机上的状压\(dp\) 设\(f[i][j][s]\)表示长度为\(i\)的串,匹配到了\(AC ...
- UVALive - 4126 Password Suspects (AC自动机+状压dp)
给你m个字符串,让你构造一个字符串,包含所有的m个子串,问有多少种构造方法.如果答案不超过42,则按字典序输出所有可行解. 由于m很小,所以可以考虑状压. 首先对全部m个子串构造出AC自动机,每个节点 ...
- [HNOI2006]最短母串 (AC自动机+状压)
Description 给定n个字符串(S1,S2,„,Sn),要求找到一个最短的字符串T,使得这n个字符串(S1,S2,„,Sn)都是T的子串. Input 第一行是一个正整数n(n<=12) ...
- HDU 3247 Resource Archiver(AC自动机 + 状压DP + bfs预处理)题解
题意:目标串n( <= 10)个,病毒串m( < 1000)个,问包含所有目标串无病毒串的最小长度 思路:貌似是个简单的状压DP + AC自动机,但是发现dp[1 << n][ ...
随机推荐
- mongoDB之监控工具mongostat及其参数的具体含义
mongostat是mongdb自带的状态检测工具,在命令行下使用.它会间隔固定时间获取mongodb的当前运行状态,并输出.如果你发现数据库突然变慢或者有其他问题的话,你第一手的操作就考虑采用mon ...
- PythonCookBook笔记——文件与IO
文件与IO 所有的程序都要处理输入与输出,涉及到文本.二进制文件.文件编码和对文件名.目录的操作. 读写文本数据 需要读写各种不同编码的文本数据,使用rt模式的open()函数. 该读写操作使用系统默 ...
- 关于wpf中popup跟随鼠标移动显示
最近在做一个画图工具,里面有一个功能是需要实现,当鼠标移动的时候在,鼠标的旁边显示坐标信息. 第一反应是想到了tooltip,但是tooltip有许多的限制,查询资料的过程中看到了popup,popu ...
- 【BZOJ1014】[JSOI2008]火星人prefix Splay+hash
[BZOJ1014][JSOI2008]火星人prefix Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个 ...
- [Phoenix] 一、快速入门
Phoenix是一个开源的HBASE SQL层.Phoeinx可以用标准的JDBC API替代HBASE client API来创建表,插入和查询HBASE中的数据. Phoenix作为应用层和HBA ...
- arm处理器的历史及现状
1 arm处理器的发展历史 arm1 arm2 arm3 arm6 arm7 arm9 arm11 arm cortex 2 arm处理器现状 arm cortex A a即application,即 ...
- java之冒泡排序
//冒泡排序(Bubble Sorting)的基本思想是:通过对待排序序列从后向前(从下标较大的元素开始),依次比较相邻元素的排序码,若发现逆序则交换,使排序码较小的元素逐渐从后部移向前部(从下标较大 ...
- access 连接数据库
前提条件 如果没有安装office的话,需要安装引擎 安装了office就不用安装引擎 连接数据库 Dim plMydb As Microsoft.Office.Interop.Access.Dao. ...
- appium()-The event firing
原文地址:https://github.com/appium/java-client/blob/master/docs/The-event_firing.md since 4.1.0 The purp ...
- indexDB操作(部分方法不太会使用)
<script type="text/javascript"> //打开数据库 function openDB(name,version){ var version = ...