leetcode-Longest Palindromic Substring

来源:互联网 发布:windows正版验证网站 编辑:程序博客网 时间:2024/06/05 02:56

LeetCode:Longest Palindromic Substring 最长回文子串

题目链接

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.

求字符串的最长回文子串

 

算法1:暴力解法,枚举所有子串,对每个子串判断是否为回文,复杂度为O(n^3)


算法2:删除暴力解法中有很多重复的判断。很容易想到动态规划,时间复杂度O(n^2),空间O(n^2),动态规划方程如下:

  • dp[i][j] 表示子串s[i…j]是否是回文
  • 初始化:dp[i][i] = true (0 <= i <= n-1);  dp[i][i-1] = true (1 <= i <= n-1); 其余的初始化为false
  • dp[i][j] = (s[i] == s[j] && dp[i+1][j-1] == true)

在动态规划中保存最长回文的长度及起点即可


 




算法3:以某个元素为中心,分别计算偶数长度的回文最大长度和奇数长度的回文最大长度。时间复杂度O(n^2),空间O(1)

算法4:Manacher算法,时间复杂度O(n), 空间复杂度O(n)

 

该算法首先对字符串进行预处理,在字符串的每个字符前后都加入一个特殊符号,比如字符串 abcd 处理成 #a#b#c#d#,为了避免处理越界,在字符串首尾加上不同的两个特殊字符(c类型的字符串尾部不用加,因为自带‘\0’),这样预处理后最终变成$#a#b#c#d#^,经过这样处理后有个好处是原来的偶数长度和奇数长度的回文在处理后的字符串中都是奇数长度。假设处理后的字符串为s                    此文地址

 

对于已经预处理好的字符串我们用数组p[i]来记录以字符S[i]为中心的最长回文子串向左/右扩张的长度(包括S[i]),以字符串“12212321”为例,p数组如下

s:   $   #  1  #  2  #  2  #  1  #  2  #  3  #  2  #  1  #  ^ 
 p:       1  2  1  2   5  2  1  4  1   2  1  6  1   2  1  2  1


     可以看出,P[i]-1正好是原字符串中回文串的总长度, 如果p数组已知,遍历p数组找到最大的p[i]就可以求出最长回文的长度,也可以求出回文的位置

 

下面给出求p[]数组的方法:

设id是当前求得的最长回文子串中心的位置,mx为当前最长回文子串的右边界(回文子串不包括该右边界),即mx = id + p[id]。记j = 2*id – i ,即 j 是 i 关于 id 的对称点。

 

1、 当i < mx 时,如下图。此时可以得出一个非常神奇的结论p[i] >= min(p[2*id - i], mx - i),下面我们来解释这个结论

 

image

如何根据p[j]来求p[i]呢,又要分成两种情况

 

(1.1)当mx – i > p[j], 这时候以S[j]为中心的回文子串包含在以S[id]为中心的回文子串中,由于 i 和 j 对称,以S[i]为中心的回文子串必然包含在以S[id]为中心的回文子串中,所以 P[i]  至少等于 p[j], 后面的再继续匹配。如下图

注:这里其实p[i]一定等于p[j],后面不用再匹配了。因为如果p[i]后面还可以继续匹配,根据对称性,p[j]也可以继续扩展了

 

image

 

(1.2)当mx – i <= p[j], 以S[j]为中心的回文子串不完全包含于以S[id]为中心的回文子串中,但是基于对称性可知,下图中两个绿框所包围的部分是相同的,也就是说以S[i]为中心的回文子串,其向右至少会扩张到mx的位置,也就是说 P[i] 至少等于 mx - i,至于mx之后的部分是否对称,就只能老老实实去匹配了。

注:如果mx – i < p[j] ,这时p[i]一定等于mx - i, 因为如果p[i]在mx之后还可以继续匹配,根据对称性,mx之后匹配的点(包括mx)一定会出现在my的前面,这说明p[id]也可以继续扩展了

 

image

 

2、当i >= mx, 无法对p[i]做更多的假设,只能p[i] = 1,然后再去匹配

 

算法复杂度分析:根据斜体字部分的注释,只有当mx-i = p[j]时 以及 i > mx时才要扩展比较,而mx也是在不断扩展的,总体而言每个元素比较次数是n的线性关系,所以时间复杂度为O(n)


java版
public class Solution {    public String longestPalindrome(String s) {        if(s==null||s.length()==0)            return "";        String str=preProcess(s);        int[] result=new int[2*s.length()+3];        int id=0;//目前延伸的最远(mx最大)的中心位置        int mx=0;//目前的延伸位置        int maxId=0;//拥有回文长度的中心点        for(int i=1;i<str.length()-1;i++){            result[i]= mx>i?Math.min(result[2*id-i],mx-i):1;//当i超出范围了,就初始化1            while(str.charAt(i-result[i])==str.charAt(i+result[i]))                result[i]++;            if(mx<i+result[i]){                id=i;                mx=i+result[i];            }            if(result[maxId]<result[i]){                maxId=i;            }        }        System.out.println("maxId="+maxId+",leng="+(result[maxId]-1) );        return s.substring((maxId - result[maxId])/2, (maxId - result[maxId])/2+result[maxId]-1);    }    public String preProcess(String s){        StringBuilder sb=new StringBuilder("^");        for(int i=0;i<s.length();i++){            sb.append("#").append(s.charAt(i));        }        sb.append("#&");        return sb.toString();    }}
Manacher复杂度为什么是O(n)??

里面虽然有个while循环,但是while循环执行的次数不超过n次,只有当i的回文长度result[i]超出了mx的范围了,才会有while循环执行,执行完后,mx的会更新到目前最大延伸范围,下次还是当i的回文长度超过mx(mx是向右延伸的),才执行while,mx最多被延伸到n,所有while执行最多n次。参考:http://write.blog.csdn.net/postedit/45675379

算法5:可以用后缀数组来解,在源字符串后面加一个特殊字符,然后把源字符串的反转串连接到源字符串后面,那么问题就变成了求这个新字符串的某两个后缀的最长公共前缀,该问题是典型的后缀数组应用,可以参考here,关于后缀数组请关注我的后续博客

 

 

参考资料:

Longest Palindromic Substring Part II

Manacher's ALGORITHM: O(n)时间求字符串的最长回文子串

Longest Palindromic Substring | Set 2

 

【版权声明】转载请注明出处:http://www.cnblogs.com/TenosDoIt/p/3675788.html

ps :Manacher算法其时间复杂度为何是O(N) http://www.cnblogs.com/bitzhuwei/p/Longest-Palindromic-Substring-Part-II.html

0 0
原创粉丝点击