题意:给定n个文本串,m个病毒串,文本串重叠部分可以合并,但合并后不能含有病毒串,问所有文本串合并后最短多长。

(2 <= n <= 10, 1 <= m <= 1000)

题解:

首先可以想出一个简单的位压DP : d[s][i] = min(d[ss][j] - 合并i、j的重叠部分长度)

问题就集中在了如何求出两个串x、y合并后的最短长度并且合并后不能包含病毒串。

引用一个题解:来自http://blog.csdn.net/woshi250hua/article/details/8021283

解题思路:综合题,需要用到AC自动机+状态压缩DP+Spfa。

总体思路是利用代码串和病毒串建立自动机,他们在自动机上的差别是一个末节点标记,一个不标记。然后将每个代码串尾节点看做图上的一个节点,利用自动机计算每个串其他所有串的不重叠的最短长度即两两节点间的最短距离。最后转变成TSP问题,状态压缩DP解之。

第一眼看到那个n,小等于10,soga,状态压缩,稍微思考下就能将问题转换成这样一个模型:n个串必须都选且选一次,求这n个串的排列使得组合成的串不包含病毒串并且长度最小。啊哈,这不是TSP问题吗?是的,你没有看错,转换成了TSP问题。

转换成TSP问题之后,我们想的是怎么让长度尽量小,考虑将两个代码串重叠起来。两个代码串a,b的前缀和后缀可能相等,他们组成的最短不包含病毒串的字符串c,前面部分为a,后面部分为b,这时候再来个d代码串要和前面两个合体,那么就成c和d的重叠问题了。

接下来我们要做的怎么让代码串a和代码串b组成的串c长度最小且不包含病毒串呢?我一开始用kmp来找两个串的相等前缀、后缀,然后组成串去ac自动机中匹配。然后一瞬间我就觉得我自己脑残了,这不是让ac自动机退化成kmp和字典树了吗!因为ac自动机上的一个节点到根的路径代表一个字符串,假设串a的末尾节是p,b的末尾节点是q,接着我们要做是在p点利用next数组转移到q,我们得到一个结论:从p到q所走的路径便是b除开与a重叠部分的那个后缀,如a为aaabb,b为bbaaa,那么路经就代表串b的aaa子串。我们怎么保证从p点走到q点,中间走过的路径表示的串一定是串b的后缀呢?两种情况:1、a是b的子串,这时候我们不会用到fail指针,显然可以 2、我们需要用到fail指针,每次用fail指针找到下一个匹配的位置假设是failx,failx节点到根节点所表示的串便是我们走过路径的最长后缀,这样一直找找到节点q,点q到根节点所表示的串遍是我们走过路径的最长后缀,然后上面的结论便得证。

总而言之,我们在ac自动机上走过的路径可以表示一个串,设为S,到达点p,那么点p到根节点这条路径所表示的串s,s为S的后缀。为用路径代表一个串是ac自动机优美之处。

我们从上面说的p点走到q点会有很多路径,要保证走过的路径长度最小即b串于a串的不重叠部分最短,要用到spfa,其实本题就退化成普通的Bfs,因为没有松弛操作。这样得到就可以得到各串相互之间的最短距离,然后就变成了很普通的TSP。

关键就在于:将文本串和病毒串建在同一个自动机上,然后从一个文本串i的末尾节点x走到另一个文本串j的末尾节点y,只能顺着next走,中途不经过任何病毒末端节点,并且路径最短。

从x走到y,这就相当于保证了文本串i、j必然存在于新构造出来的字符串中(也就是路径)。

走的时候路径上不是可能有不是i、j的字符串吗?

是的!会有可能走到其他的串,然后通过next走到了根节点,然后就相当于不加限制地走到其他串中了。

但是这样只会比最优解更长,答案根本不会取到它。

为什么是顺着next走呢?

我的理解是这样的:通过AC自动机,如果一个点没有相应的0孩子或1孩子,已经在求fail的时候把fail所对应的孩子当成是它自己的孩子了。

也就是说,它走到另一个串的前提是自己没有这个孩子,而fail有。

这样走就相当于跳过了两个串的公共部分,走到了另一个串。

我打的时候WA了一次,错在了我让它可以顺着fail到达另一个串(即使它自己本身也有这个孩子)。

这样的错误证明了只能通过next走,因为通过next走保证了它走过的不是病毒串(因为病毒末端我们不走),如果它现在是1,末端是0(病毒末端),通过fail到了串101,则走过的101包含了自己本来的病毒末端。也就是说,这样做保证了走只能走完一个串(除非要走的点这个串没有,那就跳到了另一个串)。

 #include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std; const int N=,M=,S=,INF=(int)1e9;
int n,m,num,com[N][N],last[N],len[N],dis[S],d[][N];
bool in[S];
char s[S];
struct node{
int fail,son[];
bool bk;
}a[S];
queue<int> q; int minn(int x,int y){return x<y ? x:y;}
void clear(int x)
{
a[x].bk=a[x].fail=a[x].son[]=a[x].son[]=;
} int sum=;
void trie(char *c,bool bk,int id)
{
int x=,l=strlen(c);
sum+=l;
for(int i=;i<l;i++)
{
int ind=c[i]-'';
if(!a[x].son[ind])
{
++num;
clear(num);
a[x].son[ind]=num;
}
x=a[x].son[ind];
}
a[x].bk=bk;
if(!bk) last[id]=x,len[id]=l;
} void buildAC()
{
while(!q.empty()) q.pop();
q.push();
while(!q.empty())
{
int x=q.front();q.pop();
int fail=a[x].fail;
for(int i=;i<=;i++)
{
if(a[x].son[i])
{
a[a[x].son[i]].fail=x ? a[fail].son[i] : ;
q.push(a[x].son[i]);
}
else a[x].son[i]=a[fail].son[i];
}
}
} void spfa(int u)
{
while(!q.empty()) q.pop();
memset(dis,,sizeof(dis));
memset(in,,sizeof(in));
int st=last[u];
q.push(st);
dis[st]=;in[st]=;
while(!q.empty())
{
int x=q.front();in[x]=;q.pop();
for(int i=;i<=;i++)
{
int y=a[x].son[i];
if(a[y].bk== && dis[y]>dis[x]+)
{
dis[y]=dis[x]+;
if(!in[y]) in[y]=,q.push(y);
}
}
//有下面这段是错的,要保证走就走完一个串,除非要走的节点这个串没有,否则就可能路径上有病毒末端。
// int fail=a[x].fail;
// if(a[fail].bk==0 && dis[fail]>dis[x])
// {
// dis[fail]=dis[x];
// if(!in[fail]) in[fail]=1,q.push(fail);
// }
}
for(int i=;i<n;i++)
com[u][i]=len[i]-dis[last[i]];
} int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
while()
{
scanf("%d%d",&n,&m);
if(!n && !m) return ;
num=;
clear();
for(int i=;i<n;i++)
{
scanf("%s",s);
trie(s,,i);
}
for(int i=;i<=m;i++)
{
scanf("%s",s);
trie(s,,i);
}
buildAC();
for(int i=;i<n;i++) spfa(i);
int ans=INF;
memset(d,,sizeof(d));
for(int i=;i<n;i++) d[(<<i)][i]=len[i];
for(int s=;s<(<<n);s++)
{
for(int i=;i<n;i++) if((<<i)&s)
{
for(int j=;j<n;j++) if(!((<<j)&s))
{
d[s+(<<j)][j]=minn(d[s+(<<j)][j],d[s][i]+len[j]-com[i][j]);
}
if(s==(<<n)-) ans=minn(ans,d[s][i]);
}
}
printf("%d\n",ans);
}
return ;
}

【hdu3247-Resource Archiver】位压DP+AC自动机+SPFA的更多相关文章

  1. BZOJ 1559 JSOI2009 密码 状压dp+AC自动机+搜索

    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1559 分析: 这个题意真的是很**啊!!!直接说每一个字符串至少出现一次不就好了吗... ...

  2. HDU3247 Resource Archiver —— AC自动机 + BFS最短路 + 状压DP

    题目链接:https://vjudge.net/problem/HDU-3247 Resource Archiver Time Limit: 20000/10000 MS (Java/Others)  ...

  3. 咕咕(数位dp+AC自动机)

    咕咕(数位dp+AC自动机) 若一个字符串的字符集合是0~m-1,那么称它为m进制字符串.给出n个m进制字符串\(s_i\),每个字符串的权值为\(v_i\).对于另一个m进制字符串\(S\),设\( ...

  4. 洛谷$P4045\ [JSOI2009]$密码 $dp$+$AC$自动机

    正解:$dp$+$AC$自动机+搜索 解题报告: 传送门$QwQ$ 首先显然先建个$AC$自动机,然后考虑设$f_{i,j,k}$表示长度为$i$,现在在$AC$自动机的第$j$个位置,已经表示出来的 ...

  5. HDU3247 Resource Archiver (AC自动机+spfa+状压DP)

    Great! Your new software is almost finished! The only thing left to do is archiving all your n resou ...

  6. 【HDU3247】 Resource Archiver(DP+AC自动机+最短路)

    Resource Archiver Time Limit: 10000MS   Memory Limit: 100000KB   64bit IO Format: %I64d & %I64u ...

  7. HDU3247 Resource Archiver(AC自动机+BFS+DP)

    题目,求最短的包含所有n个DNA片段且不包含任何一个病毒片段的序列. 容易用所有DNA片段和病毒片段建一个AC自动机,构造fail时处理一下各个结点后缀是DNA或者病毒的情况,然后dp[S][u]表示 ...

  8. HDU-3247 Resource Archiver(AC自动机+BFS)

    Description Great! Your new software is almost finished! The only thing left to do is archiving all ...

  9. [HDU3247]Resource Archiver

    AC自动机+状压DP 首先对所有串建AC自动机,然后对于每个资源串,算出从串末走到其他资源串末所需的距离(中途避开非法点) 也就是算出两两间的距离...然后就变成旅行商问题了. 计算距离的时候要考虑一 ...

随机推荐

  1. MongoDB 数据类型

    MongoDB支持许多数据类型的列表下面给出: String : 这是最常用的数据类型来存储数据.在MongoDB中的字符串必须是有效的UTF-8. Integer : 这种类型是用来存储一个数值.整 ...

  2. python学习笔记1

    python3.3使用urllib2报错 no module named urllib2,原因是python3中将urllib2换成了request. 所以要使用import urllib.reque ...

  3. Mysql去除重复

    常用的有两种方法,第一种就是select distinct name from table.但是有时候我们要返回多个字段时就用第二种方法select *, count(distinct name) f ...

  4. [转] Matlab中给信号加高斯白噪声的方法

    MATLAB中产生高斯白噪声非常方便,可以直接应用两个函数,一个是WGN,另一个是AWGN.WGN用于产生高斯白噪声,AWGN则用于在某一信号中加入高斯白噪声. 1. WGN:产生高斯白噪声 y = ...

  5. C++ 学习笔记(一)

    只是记录自己学习C++ 笔记实例来自<c++ primer> 1.static: static 局部对象确保不迟于在程序执行流程第一次经过该对象的定义语句时进行初始化.这种对象一旦被创建, ...

  6. objective-c自学总结(一)---面向对象

    本人大二本科在读,利用一个月多一点的时间对OC语言基础进行了自学,在下一阶段UI学习开始之前, 对这一阶段的自学进行一些总结.在此特别感谢刘晓斌学长和无线互联3G学院 首先说一下对OC的整体感觉,这是 ...

  7. responsive layout

    http://cssdeck.com/labs/7wsdvxdc http://getbootstrap.com/css/ http://getbootstrap.com/2.3.2/scaffold ...

  8. c++ _beginthread

    c++多线程编程 #include <windows.h> #include <process.h> /* _beginthread, _endthread */ #inclu ...

  9. VBS基础篇 - 内置函数

    Date/Time 函数 函数 描述 CDate 把有效的日期和时间表达式转换为日期(Date)类型. Date 返回当前的系统日期. DateAdd 返回已添加指定时间间隔的日期. DateDiff ...

  10. CoffeeRobotTeam项目组报告

    一.小组分工 模块 任务 责任人 备注 报告 需求分析 熊振威 功能分析 熊振威 项目报告 熊振威 人机界面 秦勤.洪超 单元测试 姜进.张文强 机器人代码 机器人类 徐意.余拥军.孙智博 机器人运动 ...