最短母串
Posted WindZR
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最短母串相关的知识,希望对你有一定的参考价值。
E. 最短母串
题目描述
原题来自:HNOI 2006
给定n个字符串 ,要求找到一个最短的字符串s ,使得这 n个字符串都是s的子串。
输入格式
第一行是一个正整数n,表示给定的字符串的个数;
以下的n行,每行有一个全由大写字母组成的字符串。
输出格式
只有一行,为找到的最短的字符串。
在保证最短的前提下,如果有多个字符串都满足要求,那么必须输出按字典序排列的第一个。
样例
样例输入
2
ABCD
BCDABC
样例输出
ABCDABC
析:这是道AC自动机好题,同时利用了状压的思想,我们将 flag 置为 (1<<(i-1)) ,那么当我们搜索到的状态 s==((1<<n)-1) 时,即为满足条件的最短母串 (状压??,没错,就是个比较不一样的状压题)
这道题的 bfs 相当妙,我们首先开两个队列,Q1, Q2 ,Q1用来存储当前的位置,Q2用来枚举状态,那么我们的边界条件就是 Q2.top()==(1<<n)-1;
下面我详细的说一下 bfs 的过程,首先,若以当前点为起点的 son[i] 没有被遍历过,那么我们首先将他的 vis 数组=1,然后将其 (包括其更新的状态 ) 放入队列,这里同时用到了两个数组,fa 用来记录每次连边的 father,这个在最后输出结果时有用,net 用来记录每次连边的字符,
(显然,我们如果直接存储一段字符串是很难处理的,那么我们就存储字符), my 表示当前的层数,在每一次搜寻结束后累加;
接下来是我们输出答案的过程,这里可能有些难理解,建议自己手模一下,首先,我们要理解 get_fail() 中的操作:
if(!v) use[u].son[i]=use[Fail].son[i];
这里的操作就是我们利用 bfs 计算的正确性所在,如果我们现在的节点没有这个儿子,那么他的儿子就是他的 Fail 指针的儿子,如图:
这样我们就可以在遍历到 A 的时候将 B 的状态加入队列;
蓝色箭头表示 在遍历到 A 的时候将 B 加入了队列,并更新了状态
绿色箭头表示输出时回溯的过程
看一下输出的过程:
但是我们注意到我们在存储的过程中:
这里为什么不用 tot,要用 my 呢?
首先我们要明确一个事情,每次我们将一个字符 Push 进队列的时候,都相当于从这个点向外连了一条边,但是,有些边可能是重复的,那么我们最终需要的答案就是当 t==(1<<n)-1 时,这是很显然当前的字符为最终答案的末尾,那么我们就要从当前这个点连边的 father
不停向上回溯,最终输出答案。最后再解释一下为什么从 my 开始回溯,因为我已知当前点为末尾,那么我当前的 my 一定与我的最后一条连我的边的编号相同!!,到这里应该解释的比较清楚了;
哦,最后还有一点,就是可能会有重复的单词,那我们在初始化的时候就要:
use[now].flag|=(1<<(pos-1));
这样无论如何我们都能将其加入计算
代码:
以上是关于最短母串的主要内容,如果未能解决你的问题,请参考以下文章