原文链接:英文版链接

  首先,我们将字符串S中插入符号“#”转化成另一个字符串T。

  比如:S = "abaaba",T = “#a#b#a#a#b#a#”。

  为了找到最长回文字串,我们需要围绕Ti进行扩展,Ti-d...Ti+d是一个回文,很明显d是围绕Ti形成的回文的长度。

  将每个回文的长度用数组P存起来,这样,P[i]就代表围绕Ti的回文长度,最长回文字串将会是P中的最大元素。

  用上面的例子,我们得到的P的结果是(从左至右):

T = # a # b # a # a # b # a #
P = 0 1 0 3 0 1 6 1 0 3 0 1 0

  看P的结果,我们可以发现,最长回文子串就是"abaaba",从而可以得出就是P6 = 6。

  插入“#”后,字符串的长度就都转化成奇数长度了(请注意:这只是为了更好的示范该算法。译者注:我发现这是为了避免类似"aa"这样的回文无法正确用P表示的情况)。

  现在,想象一下给"abaaba"划一根竖线,有没有发现P的数值根据这根线中心对称?不仅仅是这个字符串,像回文"aba"也是如此,P中的这些数字也反应出了相似的对称性。这是巧合吗?有时候是,有时候不是。这个现象只在一种条件下是符合的,但不管怎么样这是一个不小的进步,这样我们就可以排除掉一些P[i]的值。

  我们从一个更加包含更多回文,更能说明问题的一个例子出发,假设S = “babcbabcbaccba”。

  上面的图片显示T从字符串S转化过来。假设这个状态下P已经部分完成了,竖实线表示回文“abcbabcba”的中心(C),虚线表示各自的左边缘(L)和右边缘(R),现在我们正在索引i的位置,并且围绕C它的映射是i',那么我们如何马上计算出P[i]?

  假设我们已经到达索引i = 13,我们需要计算出p[13](上面的?标记的位置),我们先看看它围绕C的映射i'=9。

  绿色的横线分别代表由i和i'为中心的回文所占的区域,我们发现i的镜像P[i'] = p[9] = 1,很明显,由于回文环绕其中心的对称性,我们得出P[i]也必须是1。

  由于对称性,从上面很明显可以得出P[i] = p[i'] = 1,实际上,从C开始的后三个元素都可以从对称性得出它们的值(P[ 12 ] = P[ 10 ] = 0, P[ 13 ] = P[ 9 ] = 1, P[ 14 ] = P[ 8 ] = 0)。

 

      现在我们在索引i = 15,以C为中心它的镜像是i' = 7,那么是否P[15] = P[7] = 7?

  现在我们在索引i = 15,P[i]的值是多少?如果我们依据对称性,P[i]的结果应该跟P[i']同为7,但这是错误的。如果我们以T15为中心扩展,我们会得到回文“a#b#c#b#a”,它实际上比从对称性得出的结果7要短,为什么呢?

        有色线条表示了以i和i'为中心的区域,实绿线表示由于对称性两者必须相同的区域,红实线表示两边可能不相同的区域,虚绿线表示超过了中心的区域

  很明显两根实绿线表示的区域必须相同,超过了中心的区域也必须对称(虚绿线表示),这里必须注意P[i']是7,它超出了以C为中心的回文的左边缘L(实红线部分),这样的话它不再遵循回文的对称性了,我们只知道P[ i ] ≥ 5,为了找到P[i]的值我们必须越过右边缘(R)进行特征对比。在这里,既然P[21] != P[1],得出结论P[i] = 5。

  这样我们就得出这个算法的核心部分了:

if P[ i' ] ≤ R – i,
then P[ i ] ← P[ i' ]
else P[ i ] ≥ P[ i' ]. (这里我们必须越过右边界R寻找P[i]的值)

  很优雅吧?如果这句话理解了,你就理解了这个算法的核心部分了,这也是最难的部分。

  最后的部分是我们该什么时候把C和R的位置往右移动,这个很简单:

如果以i为中心的回文越过了R,我们将C更新为i(回文中心),然后将R扩展为新回文的右边缘。

  每移动一步,有两种可能。如果P[ i ] ≤ R – i,我们设置P[i] = p[i'],这个只需要一步。否则我们尝试将回文的中心移到i,并且从右边缘R开始扩展之。扩展R(内部循环)最多需要N步,然后定位和测试中心点也需要N步。最终,这个算法可以保证在2*N步之内完成,得到了一个线性时间解。

  现在我们可以通过P获取最长回文的长度maxLen及其中间位置的索引i,那么我们应该截取哪一段字符串才是我们需要的回文子串呢。

  对比一下S和T,我们会发现,如果字母A在T中的位置为n的话,那么它在S中的位置就是(n - 1)/2,那是不是截取字符串的起始位置就是(i - maxLen - 1)/2?其实不是的,注意到在T中回文的第一个字母肯定是“#”,所以我们需要先把位置往后移一位,到“#”后面的第一个字母,也就是我们需要的回文的起始字母(比如"#a#a#",我们需要的是"a#a"的起始位置而不是"#a#a#"的起始位置),那最终的结果就是(i - maxLen + 1 - 1)/2,也就是(i - maxLen)/2。

  下面奉上javascript的算法。

        function maxPalin(t) {
var c = 0,
R = 0,
p = [0],
s = '#' + t.split('').join('#') + '#',
maxLen = 0,
center;
for (var i = 1, len = s.length; i < len; ++i) {
var iMirror = 2*c - i;
p[i] = R > i ? Math.min(R - i, p[iMirror]) : 0;
while(s[i - 1 - p[i]] && (s[i - 1 - p[i]] === s[i + 1 + p[i]])) {
++p[i];
}
if (p[i] > R - i) {
c = i;
R = i + p[i];
}
} for (i = 1; i < len; ++i) {
if (p[i] > maxLen) {
maxLen = p[i];
center = i;
}
}
return t.substr((center - maxLen) / 2, maxLen);
}

最长回文子串O(n)算法的更多相关文章

  1. Leetcode 5. Longest Palindromic Substring(最长回文子串, Manacher算法)

    Leetcode 5. Longest Palindromic Substring(最长回文子串, Manacher算法) Given a string s, find the longest pal ...

  2. 最长回文子串的Manacher算法

    对于一个比较长的字符串,O(n^2)的时间复杂度是难以接受的.Can we do better? 先来看看解法2存在的缺陷. 1) 由于回文串长度的奇偶性造成了不同性质的对称轴位置,解法2要对两种情况 ...

  3. 51nod1089(最长回文子串之manacher算法)

    题目链接: https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1089 题意:中文题诶~ 思路: 我前面做的那道回文子串的题 ...

  4. 求最长回文子串:Manacher算法

    主要学习自:http://articles.leetcode.com/2011/11/longest-palindromic-substring-part-ii.html 问题描述:回文字符串就是左右 ...

  5. 最长回文子串(Manacher算法)

    回文字符串,想必大家不会不熟悉吧? 回文串会求的吧?暴力一遍O(n^2)很简单,但当字符长度很长时便会TLE,简单,hash+二分搞定,其复杂度约为O(nlogn), 而Manacher算法能够在线性 ...

  6. 计算字符串的最长回文子串 :Manacher算法介绍

    转自: http://www.open-open.com/lib/view/open1419150233417.html Manacher算法 在介绍算法之前,首先介绍一下什么是回文串,所谓回文串,简 ...

  7. 51Nod 1089 最长回文子串 V2 —— Manacher算法

    题目链接:https://vjudge.net/problem/51Nod-1089 1089 最长回文子串 V2(Manacher算法) 基准时间限制:1 秒 空间限制:131072 KB 分值:  ...

  8. 51 Nod 1089 最长回文子串(Manacher算法)

    1089 最长回文子串 V2(Manacher算法)  基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题  收藏  关注 回文串是指aba.abba.cccbccc.aaa ...

  9. 最长回文子串 —— Manacher (马拉车) 算法

    最长回文子串 回文串就是原串和反转字符串相同的字符串.比如 aba,acca.前一个是奇数长度的回文串,后一个是偶数长度的回文串. 最长回文子串就是一个字符串的所有子串中,是回文串且长度最长的子串. ...

随机推荐

  1. WCF 入门 (17)

    前言 看的是入门视频,就希望培养一个学习的习惯吧. 前段时间看了微软的SurfaceBook的视频,被惊艳到了,但是我没钱买啊... 第17集 WCF中未经处理的异常 Unhandled except ...

  2. mysql-5.7.9-winx64 MySQL服务无法启动,服务没有报告任何错误的解决办法

    问题背景 最新解压版本的mysql 解压安装的时候报错 D:\mysql-5.7.9-winx64\bin>net start mysql MySQL 服务正在启动 . MySQL 服务无法启动 ...

  3. 虚拟机 vlan trunk 特性

    1. 功能 1)允许不同vlan的network下的虚拟机之间通信.一般情况下,虚拟机只能在相同vlan的网络下通信. 2)允许虚拟机发送vlan报文. 2. 组网图 虚拟机出来的tap设备连接到tb ...

  4. 坑爹的BFC;块格式上下文

    Formatting context(FC) Formatting context 是 W3C CSS2.1 规范中的一个概念.它是页面中的一块渲染区域,并且有一套渲染规则,它决定了其子元素将如何定位 ...

  5. Canvas识别相似图片

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  6. 学习笔记--(平衡树)splay

    坑爹的splay,毁我青春,耗我钱财,颓我精力 是一种用于保存有序集合的简单高效的数据结构.伸展树实质上是一个二叉查找树.允许查找,插入,删除,删除最小,删除最大,分割,合并等许多操作,这些操作的时间 ...

  7. (转)eclipse项目导入到android studio中

    原文:http://www.cnblogs.com/lao-liang/p/5016541.html?utm_source=tuicool&utm_medium=referral Androi ...

  8. “面向对象"和"面向过程"到底有什么区别?

    链接:http://www.zhihu.com/question/27468564/answer/101951302 当软件还非常简单的时候,我们只需要面向过程编程: 定义函数函数一函数二函数三函数四 ...

  9. Maven学习笔记-01-Maven入门

    一 Maven的基本概念 Maven(翻译为"专家","内行")是跨平台的项目管理工具.主要服务于基于Java平台的项目构建,依赖管理和项目信息管理. 1  项 ...

  10. 高效图片轮播,两个imageView实现

    本文是投稿文章,作者:codingZero 导语 在不少项目中,都会有图片轮播这个功能,现在网上关于图片轮播的框架层出不穷,千奇百怪,笔者根据自己的思路,用两个imageView也实现了图片轮播,这里 ...