LeetCode(4) || Longest Palindromic Substring 与 Manacher 线性算法

题记

本文是LeetCode题库的第五题,没想到做这些题的速度会这么慢,工作之余全部耗在这上面了,只怪自己基础差。本文主要介绍使用Manacher线性算法来求解字符串的最长回文子字符串。

题目

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.

解题思路

  • 题目意思比较简单,假设字符串S最大长度是1000,求出该字符串的最长回文字符串。假设存在唯一的最长回文字符串。
  • 回文有两种情况,第一种"aba",第二种"abba",如果没有较有效的方法,在后续的求解中这两种情况将大大增加解题难度。Manacher算法使用了一种比较有效的方法。假设字符'#'没有出现在S中,将'#'依次插入到字符串S中,比如"aba"->"#a#b#a#","abba"->"#a#b#b#a#",这样就巧妙的将两种情况合在一起解决了。
     public char[] expandString(String s){
int len = s.length();
char[] cs = new char[len*2+3];
for(int i = 0; i < len; i++){
cs[2*i+1]='#';
cs[2*i+2]=s.charAt(i);
}
cs[0]='*';
cs[2*len+1]='#';
cs[2*len+2]='?';
return cs;
}
public String shrinkString(char[] cs){
int len = cs.length;
char [] shrinkChars = new char[len];
int j=0;
for(int i = 0; i < len;i++){
if (cs[i] != '#'){
shrinkChars[j++] = cs[i];
}
}
return new String(shrinkChars).trim();
}
  • 解此题最暴力的事情就是使用两个for循环,计算复杂度在O(n2),我们首先排除这种野蛮的方法,否则做这题就没意义了。
  • 我想到的方法是计算复杂度在O(n*log(n))的一种方法,方法大致思路如下:
    • 遍历字符串S,假设当前位置为i,将i向两边开始遍历,利用回文关于重点对称的特性来求取回文, while(cs[i+j]==cs[i-j]) j++;
    • 这里数组P是存放当前位置的最长回文半径,建下文的Manacher线性算法。
    public String longestPalindrome(String s) {

        char[] cs = expandString(s);
int len = cs.length;
int [] p = new int[len];
int max = 0;
for(int i = 1; i <len-1 ; i++){
p[i]=1;
while(cs[i+p[i]]==cs[i-p[i]]) p[i]++;
if (p[i] > p[max]){
max = i;
} } int lenMax = p[max];
char [] result = new char[2*lenMax-1];
result[lenMax-1] = cs[max];
for(int i = 1 ; i < lenMax ; i++){
result[i+lenMax-1] = cs[max+i];
result[lenMax-1-i] = cs[max+i];
} return shrinkString(result);
}
  • 第二种方法虽然在LeeCode中解出了最大回文字符串,但是在求解过程中由于重复求解还是浪费了很多时间,比如S="abababa",S是关于S[3]回文的,当遍历S[1]时候,由于对称关系S[5]的一部分回文已经确立了,所以无需再S[4]到S[6]之间就无需再遍历,只需完成映射即可。这就是Manacher线性算法的核心内容,Manacher线性算法最大程度上使用了回文的特性,算法内容在下文中介绍。

Manacher 线性算法

具体的Manacher线性算法详见《最长回文子串(Longest Palindromic Substring)》。

利用一个辅助数组 arr[n],其中 arr[i] 记录的是以 str[i] 为中心的回文子串长度。当计算 arr[i] 的时候,arr[0...i-1] 是已知并且可被利用的。Manacher 核心在于:用 mx 记录之前计算的最长的回文子串长度所能到达的最后边界,用 id 记录其对应的中心,可以利用回文子串中的回文子串信息。

假设 id 与 mx 已经得出,当计算以 str[i] 为中心回文子串长度时,因为已经可以确定绿色部分已经是回文子串了,所以可以利用以 str[j] 为中心回文子串长度即 arr[j]。在上图的情况下,所以可以从箭头所指出开始比较。还有一种情况:

这种情况下,不能直接利用以 str[j] 为中心回文子串长度即 arr[j],因为以 id 为中心回文子串长度只计算到了绿色箭头所指之处,所以能力利用的信息是 mx-i,比较 mx-i 之后的字符。

Manacher算法代码如下:

     public String longestPalindromeOne(String s) {

         char[] cs = expandString(s);
int len = cs.length;
int [] p = new int[len];
int id = 0;
int max = 0;
for(int i = 1; i <len-1 ; i++){
if (i < p[id]+id){
p[i]=Math.min(p[id]+id-i, p[2*id-i]);
}else{
p[i]=1;
} while(cs[i+p[i]]==cs[i-p[i]]) p[i]++; if( p[i]+i> p[id]+id ){
id = i;
} if (p[i] > p[max]){
max = i;
} } int lenMax = p[max];
char [] result = new char[2*lenMax-1];
result[lenMax-1] = cs[max];
for(int i = 1 ; i < lenMax ; i++){
result[i+lenMax-1] = cs[max+i];
result[lenMax-1-i] = cs[max+i];
} return shrinkString(result);
}

关于Manacher算法要补充说明一下几点:

  • 为防止在遍历while(cs[i+p[i]]==cs[i-p[i]]) p[i]++;时,需要对字符串进行处理,在字符串S[0]添加字符'*',在字符串末尾S[N+1]添加字符'?',这样房子因为S本身为回文而使得遍历出错。
  • 数组p表示字符的回文半径,比如S="#a#b#c#g#c#h" 数组为P="121212141211",S[7]=g 的 回文半径为p[7]=4
  • id表示前一个回文字符串的中心字符的编号,那么p[id]为前一个回文字符的半径。比如当i=8时,前一个回文字符串为"#c#g#c#",p[id]=4。
  • p[i]=Math.min(p[id]+id-i, p[2*id-i]); 的意思就是,如果i位于前一个回文字符串的半径内(即 i < p[id]+id),i到回文字符的两个最远边界(由于回文字符关于id位置对称,最左边为p[2*id-i],最右边为p[id]+id-i)的最小距离为Math.min(p[id]+id-i, p[2*id-i])。根据对称关系,如果i到Math.min(p[id]+id-i, p[2*id-i])的元素不需要再进行遍历,只需直接从Math.min(p[id]+id-i, p[2*id-i])开始后续的回文检查while(cs[i+p[i]]==cs[i-p[i]]) p[i]++;。这样做的好处是大大减少了重复步骤,降低计算复杂度。
  • 每一个id对应的是一个回文字符串,只要在记录遍历过程中p[id]最大的id即可获取最长的回文字符串。

LeetCode(4) || Longest Palindromic Substring 与 Manacher 线性算法的更多相关文章

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

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

  2. [LeetCode] Longest Palindromic Substring(manacher algorithm)

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

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

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

  4. 【JAVA、C++】LeetCode 005 Longest Palindromic Substring

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

  5. [LeetCode][Python]Longest Palindromic Substring

    # -*- coding: utf8 -*-'''__author__ = 'dabay.wang@gmail.com'https://oj.leetcode.com/problems/longest ...

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

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

  7. 最长回文子串-LeetCode 5 Longest Palindromic Substring

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

  8. LeetCode 5 Longest Palindromic Substring(最长子序列)

    题目来源:https://leetcode.com/problems/longest-palindromic-substring/ Given a string S, find the longest ...

  9. leetcode:Longest Palindromic Substring(求最大的回文字符串)

    Question:Given a string S, find the longest palindromic substring in S. You may assume that the maxi ...

随机推荐

  1. [C#] 常用工具类——加密解密类

    using System; using System.Configuration; using System.Collections.Generic; using System.Text; using ...

  2. PowerDesigner跟表的字段加注释

    选择那个表 右键- >Properties- >Columns- >Customize Columns and Filter(或直接用快捷键Ctrl+U)- >Comment( ...

  3. poj 3294 Life Forms

    后缀数组的题目,把后缀连接起来,这个还是先二分答案,然后选取一段连续的height值,判断这些height代表的后缀有没有覆盖一半以上的字符串. 得出答案的长度之后还要在枚举连续的heigh,判断有没 ...

  4. 用WidgeDuino创建一个SCADA(监控与数据採集)系统

    WidgeDuino – 近期在Kickstarter上亮相 – 是一个智能的易配置的窗体- 基于Microsoft Windows平台和基于像 Atmel-based Arduino board 的 ...

  5. 在android客户端加载html源代码总结

    在实际应用中,客户端要从网页上获取数据是常见的事,如果要解析网页上的html文档,那么首先得获取html源码,然后现在一般使用Jsoup来转换成Document文档来进行解析,本文主要讨论如何使用Js ...

  6. HTTP 404 - 未找到文件 怎么样解决

    找不到网页  您要查看的网页可能已被删除.名称已被更改,或者临时不可用.  -------------------------------------------------------------- ...

  7. Java实现简单版SVM

    Java实现简单版SVM 近期的图像分类工作要用到latent svm,为了更加深入了解svm,自己动手实现一个简单版的.         之所以说是简单版,由于没实用到拉格朗日,对偶,核函数等等.而 ...

  8. careercup-栈与队列 3.3

    3.3 栈就像叠盘子,当盘子叠得太高时,就会倾斜倒下.因此,在真实的世界中,当一叠盘子 (栈)超过了一定的高度时,我们就会另起一堆,再从头叠起.实现数据结构SetOfStacks 来模拟这种情况.Se ...

  9. IOS Remote Notification

    1. 本地证书合成 rm *.pem echo "export cert..." openssl pkcs12 -clcerts -nokeys -out push_cert.pe ...

  10. iOS 应用性能测试的相关方法、工具及技巧

    用户不喜欢等待.他们不关心也不应该关心一个应用初始化的时候需要什么,他们只想尽快地完成他们的任务.你的应用应该几乎是瞬间启动的,其界面应当如丝般顺滑.在充满竞争的软件市场中,应用的性能是关键的优势之一 ...