马拉车算法 (最长回文串 例题 密码截获)
Manacher 算法
manacher 算法,我们习惯叫他 “马拉车”算法。
Manacher 算法的应用范围比较狭窄,但是它的思想和拓展 kmp 算法有很多共通之处,所以在这里介绍一下。
Manacher 算法是查找一个字符串的最长回文子串的线性算法。
在介绍算法之前,首先介绍一下什么是回文串,所谓回文串,简单来说就是正着读和反着读都是一样的字符串,比如 abba,noon 等等,一个字符串的最长回文子串即为这个字符串的子串中,是回文串的最长的那个。计算字符串的最长回文字串最简单的算法就是枚举该字符串的每一个子串,并且判断这个子串是否为回文串,这个算法的时间复杂度为 O(n^3)的,显然无法令人满意,稍微优化的一个算法是枚举回文串的中点,这里要分为两种情况,一种是回文串长度是奇数的情况,另一种是回文串长度是偶数的情况,枚举中点再判断是否是回文串,这样能把算法的时间复杂度降为 O(n^2),但是当 n 比较大的时候仍然无法令人满意,Manacher 算法可以在线性时间复杂度内求出一个字符串的最长回文字串,达到了理论上的下界。
1.Manacher 算法原理与实现
下面介绍 Manacher 算法的原理与步骤。
首先,Manacher 算法提供了一种巧妙地办法,将长度为奇数的回文串和长度为偶数的回文串一起考虑,具体做法是,在原字符串的每个相邻两个字符中间插入一个分隔符,同时在首尾也要添加一个分隔符,分隔符的要求是不在原串中出现,一般情况下可以用 #号。下面举一个例子:
(1)len 数组简介与性质
Manacher 算法用一个辅助数组 Len[i]表示以字符 s[i]为中心的最长回文字串的最右字符到 s[i]的长度,比如以 s[i]为中心的最长回文字串是 s[l,r],那么 Len[i]=r-i+1。
对于上面的例子,可以得出 Len[i]数组为:
Len 数组有一个性质,那就是 Len[i]-1 就是该回文子串在原字符串 S 中的长度,至于证明,首先在转换得到的字符串 T 中,所有的回文字串的长度都为奇数,那么对于以 s[i]为中心的最长回文字串,其长度就为 2*Len[i]-1,经过观察可知,s 中所有的回文子串,其中分隔符的数量一定比其他字符的数量多 1,也就是有 Len[i]个分隔符,剩下 Len[i]-1 个字符来自原字符串,所以该回文串在原字符串中的长度就为 Len[i]-1。
有了这个性质,那么原问题就转化为求所有的 Len[i]。下面介绍如何在线性时间复杂度内求出所有的 Len。
(2)Len 数组的计算
首先从左往右依次计算 Len[i],当计算 Len[i]时,Len[j] ( 0<=j<i)已经计算完毕。设 mx 为之前计算中最长回文子串的右端点的最大值,并且设取得这个最大值的位置为 mx,分两种情况:
第一种情况:i<=mx
那么找到 i 相对于 mid(id)的对称位置,设为 j,那么如果 Len[j]<mx-i,如下图:
那么说明以 j 为中心的回文串一定在以 mid 为中心的回文串的内部,且 j 和 i 关于位置 mid(id)对称,由回文串的定义可知,一个回文串反过来还是一个回文串,所以以 i 为中心的回文串的长度至少和以 j 为中心的回文串一样,即 Len[i]>=Len[j]。因为 Len[j]<mid(id)-i,所以说 i+Len[j]<mx。由对称性可知 Len[i]=Len[j]。
如果 Len[j]>=mid(id)-i,由对称性,说明以 i 为中心的回文串可能会延伸到 mx 之外,而大于 mx 的部分我们还没有进行匹配,所以要从 mx+1 位置开始一个一个进行匹配,直到发生失配,从而更新 mx 和对应的 mid(id)以及 Len[i]。
第二种情况: i>mx
如果 i 比 mx 还要大,说明对于中点为 i 的回文串还一点都没有匹配,这个时候,就只能老老实实地一个一个匹配了,匹配完成后要更新 id 的位置和对应的 mid(id)以及 Len[i]。
2.时间复杂度分析
Manacher 算法的时间复杂度分析和 Z 算法类似,因为算法只有遇到还没有匹配的位置时才进行匹配,已经匹配过的位置不再进行匹配,所以对于 T 字符串中的每一个位置,只进行一次匹配,所以 Manacher 算法的总体时间复杂度为 O(n),其中 n 为 T 字符串的长度,由于 s 的长度事实上是 str 的两倍,所以时间复杂度依然是线性的。
算法的实现
注意,为了避免更新 mid(id)的时候导致越界,我们在字符串 s 的前增加一个特殊字符,比如说‘*’,所以算法中字符串是从 1 开始的。
问题 1209: 密码截获
时间限制: 1Sec 内存限制: 128MB 提交: 81 解决: 32
题目描述
Catcher 是 MCA 国的情报员,他工作时发现敌国会用一些对称的密码 进行通信,比如像这些 ABBA,ABA,A,123321,但是他们有时会在开始或结束时加入一些无关的字符以防止别国破解。比如进行下列变化 ABBA->12ABBA,ABA->ABAKK,123321->51233214 。因为截获的串太长了,而且存在多种可能的情况 (abaaab 可看作是 aba,或 baaab 的加密形式),Cathcer 的工作量实在是太大了,他只能向电脑高手求助,你能帮 Catcher 找出最长的 有效密码串吗?
输入
测试数据有若干行字符串,包括字母,数字,符号。(字母区分大小写)
输出
与输入相对应每一行输出一个整数,代表最长有效密码串的长度。
样例输入
ABBA
12ABBA
A
ABAKK
51233214
abaaab
样例输出
4
4
1
3
6
5
版权声明: 本文为 InfoQ 作者【Five】的原创文章。
原文链接:【http://xie.infoq.cn/article/faafb3c6ab8bd73639a9a109f】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论