题目描述

Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.

即给定一个字符串,求它的最长回文子串的长度(或者最长回文子串)。

解法一

对于一个问题,一定可以找到一个傻的可爱的暴力解法,本题的暴力解法即:遍历整个字符串,以每一个字符为中心寻找以该字符为中心的最长回文子串,一次遍历下来即可获取最长回文子串,但是相应的这种方法的复杂度很糟糕O(N2)

这里值得注意的是要分开处理字符串长度为奇数或者偶数的情况

代码如下:

 public String longestPalindrome(String s) {
String max = "";
int len = s.length();
for (int i = 0; i < len; i++) {
int odd = 0;
for (int j = 1; i + j < len && i >= j; j++) {
if (s.charAt(i + j) == s.charAt(i - j))
++odd;
else
break;
}
if (2 * odd + 1 > max.length())
max = s.substring(i - odd, i + odd + 1);
int even = 0;
for (int j = 1; i + j + 1 < len && i >= j; j++) {
if (s.charAt(i + j + 1) == s.charAt(i - j))
++even;
else
break;
}
if (even * 2 + 2 > max.length())
max = s.substring(i - even, i + even + 2 > s.length() ? s.length() : i + even + 2);
} return max;
}

解法二

OK,笨的方法找到了,怎么样去提高效率。观察上面遍历的过程,对于字符串S,当以第i个字符为中心时,我们需要重新计算其最长回文子串,每个都是重头开始计算,那么能否利用回文字符串的性质来减少这种计算量从而提高整个算法的效率?答案是肯定的,下面要说的Manacher's 算法就是这样

统一奇偶

在正式开始算法之前,能否有一个方法将奇偶两种情况统一起来?为了达到这个目的,我们可以将原字符串S每个字符之间插入一个特殊字符'#',得到一个新的字符串T,如下

  • S = "abaaba", T = "#a#b#a#a#b#a#".

这样可以发现无论长度奇偶都转换为奇情况来处理

Manacher算法

开头提到的思想,当我们要找寻以Tj为中心的回文子串时,能否利用前面已经算出的以Ti(i<j)为中心的回文子串,所以我们将前面算出的中间结果存入数组P,P[i]表示已Ti为中心的回文子串的长度(不包括其自身),而最长回文子串的长度就是P中的最大值。我们接着上面的例子

我们通过字符串T将对应的P写出,由于P6 = 6,可以很容易得出最长回文子串是"abaaba"

那么我们现在的关注点就主要放在如何计算P上,当然利用解法一那样我们可以通过遍历每一个点作为中心点来获取P,但是效率很低。还是前面提到的那个思想,当计算到第i个点作为中心点时,能否利用前面已经计算过的点。

观察回文串"abaaba"所对应的P,我们可以发现一条有用的规律,以i = 6作为中心,P中的数据时关于这个中心对称的,这不是偶然,我们可以尝试"aba",我们可以发现相似的对称性质。如果这条性质可以使用,我们就可以减少重复计算P的值。

为了验证这个思想,我们举一个稍微复杂一些的例子,S = "babcbabcbaccba",我们以图展现部分计算P的过程,从中找寻规律:

假设我们已经计算出一部分P的值,图中实线表示回文子串"abcbabcba"的中心位置,而虚线则表示该子串的左右边界

现在当我们想要计算i=13时P的值,已知i关于C的对称点i',我们怎样快速求出P[ i ]?

图中给出了我们的算法进行到i = 13的时候,我们需要计算P[ 13 ],我们来观察i关于回文子串中心点C的对称点i‘ = 9

图中我们用绿线标出了分别以i'和i为中心的回文子串,可以发现由于关于C点的对称性质,很容易得出P[ i ] = P[ i' ] = 1

通过以上分析,由于关于C的对称性质,我们很开心的得出一个结论P[ i ] = P[ i' ] = 1,而且之后的三个元素也都可以利用这条性质得出P的值(P[ 12 ] = P[ 10 ] = 0, P[ 13 ] = P[ 9 ] = 1, P[ 14 ] = P[ 8 ] = 0)

现在我们需要计算P[ 15 ],而i = 15关于C的对称点是i' = 7,那么p[ 15 ] = P[ 7 ] = 7?

当我们需要计算i = 15的时候,我们利用上面总结出的规律可以得出P[ 15 ]  = P[ 7 ] = 7,但是进一步去计算P[ 15 ] 我们会发现以i = 15为中心点的最长回文子串是"a#b#c#b#a",事实是P[ 15 ] 要比对称点的子串短,这是为什么呢?

图中我们将以i'和以i为中心的子串用线标出,其中以绿线标出了严格根据中心点C对称的部分,而红线标出了超出

以C为中心点的子串的左右边界的部分,绿色的虚线部分标出了跨过中心点的部分

我们可以很清晰的发现,两个子串在绿色线标出的地方是完全对称的,同样绿色虚线部分由于中心对称也是满足的。然而P[ i' ] = 7,所以以i'为中心的子串超出了左边界,则这部分不再满足对称性质。我们只是知道P[ i ] ≥ 5,即i到右边界的长度。为了进一步得出P[ i ] 的值,我们需要向右扩展,在这里,由于P[ 21 ] ≠ P[ 7 ],所以P[ i ] = 5

我们总结一下上面发现的规律:

如果P[ i' ] ≤ R - i

那么 P[ i ] = P[ i' ]

否则 P[ i ] ≥ R - i (之后我们需要扩展出右边界来找到最终的P[ i ])

另外我们需要判断一下什么时候移动中心点C以及其右边界R

当以i为中心点的回文子串超出了右边界R时,我们将C移至i,将右边界R移至i的右边界

AC代码如下

     private String insert(String s){
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
sb.append('#');
sb.append(s.charAt(i));
}
sb.append('#'); return sb.toString();
} public String longestPalindrome(String s) {
String t = insert(s);
int[] p = new int[t.length()];
int c = 0, r = 0; // 当前中心位置以及当前中心位置的右边界
for (int i = 0; i < t.length(); i++) {
int i_mirror = 2 * c - i;
p[i] = r > i ? Math.min(r - i, p[i_mirror]) : 0;
while (i + p[i] + 1 < t.length() && i - p[i] - 1 >= 0
&& t.charAt(i + p[i] + 1) == t.charAt(i - p[i] - 1))
p[i]++;
if (i + p[i] > r) {
c = i;
r = i + p[i];
}
} int maxC = 0;
for (int i = 0; i < t.length(); i++) {
if (p[i] > p[maxC])
maxC = i;
}
return t.substring(maxC - p[maxC], maxC + p[maxC]).replace("#", "");
}

由于我们有两个变量中心点C以及右边界R,当P[ i ] ≤ R – i,我们直接以O(1)进行计算,而另一种情况则需要移动中心点和右边界,最多两者都是移动N步,所以总的时间负责度时O(2*N)即O(N)

——reference

Longest Palindromic Substring Part II

最长回文子串

最长回文子串-LeetCode 5 Longest Palindromic Substring的更多相关文章

  1. 求最长回文子串 - leetcode 5. Longest Palindromic Substring

    写在前面:忍不住吐槽几句今天上海的天气,次奥,鞋子里都能养鱼了...裤子也全湿了,衣服也全湿了,关键是这天气还打空调,只能瑟瑟发抖祈祷不要感冒了.... 前后切了一百零几道leetcode的题(sol ...

  2. 动态规划—最长回文子串LEETCODE第5题深度剖析

    动态规划对于笔者来说有很重要的意义 一.题目如下: 对于此类题目,笔者常用的的办法是先做个暴力解题思路,然后再对暴力法进行优化. 二.暴力法 //字串遍历 public static String l ...

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

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

  4. [LeetCode] 5. Longest Palindromic Substring 最长回文子串

    Given a string s, find the longest palindromic substring in s. You may assume that the maximum lengt ...

  5. 【LeetCode】5. Longest Palindromic Substring 最长回文子串

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 公众号:负雪明烛 本文关键词:最长回文子串,题解,leetcode, 力扣,python ...

  6. LeetCode:Longest Palindromic Substring 最长回文子串

    题目链接 Given a string S, find the longest palindromic substring in S. You may assume that the maximum ...

  7. leetcode 5 :Longest Palindromic Substring 找出最长回文子串

    题目: Given a string S, find the longest palindromic substring in S. You may assume that the maximum l ...

  8. [leetcode]5. Longest Palindromic Substring最长回文子串

    Given a string s, find the longest palindromic substring in s. You may assume that the maximum lengt ...

  9. [译+改]最长回文子串(Longest Palindromic Substring) Part II

    [译+改]最长回文子串(Longest Palindromic Substring) Part II 原文链接在http://leetcode.com/2011/11/longest-palindro ...

随机推荐

  1. C语言 · 最大值与最小值计算

    输入11个整数,计算它们的最大值和最小值. 样例输入 0 1 2 3 4 5 6 7 8 9 10 样例输出 10 0   #include<stdio.h> int main(){ ]; ...

  2. SQL Server 常用内置函数(built-in)持续整理

    本文用于收集在运维中经常使用的系统内置函数,持续整理中 一,常用Metadata函数 1,查看数据库的ID和Name db_id(‘DB Name’),db_name('DB ID') 2,查看对象的 ...

  3. HTML 事件(一) 事件的介绍

    本篇主要介绍HTML中的事件知识:事件相关术语.DOM事件规范.事件对象. 其他事件文章 1. HTML 事件(一) 事件的介绍 2. HTML 事件(二) 事件的注册与注销 3. HTML 事件(三 ...

  4. Spring之旅

    Java使得以模块化构建复杂应用系统成为可能,它为Applet而来,但为组件化而留. Spring是一个开源的框架,最早由Rod Johnson创建.Spring是为了解决企业级应用开发的复杂性而创建 ...

  5. 读python源码--对象模型

    学python的人都知道,python中一切皆是对象,如class生成的对象是对象,class本身也是对象,int是对象,str是对象,dict是对象....所以,我很好奇,python是怎样实现这些 ...

  6. Git初探--笔记整理和Git命令详解

    几个重要的概念 首先先明确几个概念: WorkPlace : 工作区 Index: 暂存区 Repository: 本地仓库/版本库 Remote: 远程仓库 当在Remote(如Github)上面c ...

  7. Javascript 严格模式详解

    转自http://www.ruanyifeng.com/blog/2013/01/javascript_strict_mode.html 一.概述 除了正常运行模式,ECMAscript 5添加了第二 ...

  8. form表单验证-Javascript

    Form表单验证: js基础考试内容,form表单验证,正则表达式,blur事件,自动获取数组,以及css布局样式,动态清除等.完整代码如下: <!DOCTYPE html PUBLIC &qu ...

  9. 使用gulp解决RequireJS项目前端缓存问题(二)

    1.前言 这一节,我们主要解决在上一节<使用gulp解决RequireJSs项目前端缓存问题(一)>末尾提到的几个问题: 对通过require-config.js引入的js文件修改后,没有 ...

  10. Linux.NET实战手记—自己动手改泥鳅(上)

    各位读者大家好,不知各位读者有否阅读在下的前一个系列<Linux.NET 学习手记>,在前一个系列中,我们从Linux中Mono的编译安装开始,到Jexus服务器的介绍,以及如何在Linu ...