题意:给定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. [译] Swift 的响应式编程

    原文  https://github.com/bboyfeiyu/iOS-tech-frontier/blob/master/issue-3/Swift的响应式编程.md 原文链接 : Reactiv ...

  2. 插入排序 & 快速排序

    2.1 插入排序: 接口定义: int insert_sort(void* data, int size, int esize, int (*compare)(const void* key1, co ...

  3. Python - DICT 字典排序 - OrderedDict

    官方地址: https://docs.python.org/2/library/collections.html#collections.OrderedDict >>> # regu ...

  4. C++string的使用

    在这里总结一下string的用法 String是可变长字符串,使用的时候要包含string头文件. 要想使用标准C++中string类,必须要包含 #include <string>// ...

  5. 课堂练习:给定一个十进制的正整数,写下从1开始,到N的所有整数,然后数一下其中出现“1”的个数。

    题目 1 给定一个十进制的正整数,写下从1开始,到N的所有整数,然后数一下其中出现“1”的个数. 2 要求: (1) 写一个函数 f(N) ,返回1 到 N 之间出现的“1”的个数.例如 f(12)  ...

  6. c编程之排序

    1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 typedef struct Nod ...

  7. TF-IDF与余弦相似性的应用(二):找出相似文章

    上一次,我用TF-IDF算法自动提取关键词. 今天,我们再来研究另一个相关的问题.有些时候,除了找到关键词,我们还希望找到与原文章相似的其他文章.比如,"Google新闻"在主新闻 ...

  8. js判断浏览器滚动条是否拉到底

    $(window).scroll(function(){ // 当滚动到最底部以上n像素时, 加载新内容 if ($(document).height() - $(this).scrollTop() ...

  9. windows环境下svn同步web文件[转]

    windows环境下svn同步web文件 SVN在团队开发中使用非常普遍,是一个很方便的版本控制系统. 如果要是能将SVN服务器上的数据自动发布到Web服务器,那将是整个项目开发.测试更加便捷.利用S ...

  10. 【BZOJ】【3205】【APIO2013】机器人robot

    斯坦纳树 好神啊……Orz zyf && PoPoQQQ 为啥跟斯坦纳树扯上关系了?我想是因为每个点(robot)都沿着树边汇到根的时候就全部合起来了吧= =这个好像和裸的斯坦纳树不太 ...