内容全部来自编程之法:面试和算法心得一书,实现是自己写的使用的是java
题目描述
给定一个字符串,求它的最长回文子串的长度。
分析与解法
最容易想到的办法是枚举所有的子串,分别判断其是否为回文。这个思路初看起来是正确的,但却做了很多无用功,如果一个长的子串包含另一个短一些的子串,那么对子串的回文判断其实是不需要的。
解法一
那么如何高效的进行判断呢?我们想想,如果一段字符串是回文,那么以某个字符为中心的前缀和后缀都是相同的,例如以一段回文串“aba”为例,以b为中心,它的前缀和后缀都是相同的,都是a。
那么,我们是否可以可以枚举中心位置,然后再在该位置上用扩展法,记录并更新得到的最长的回文长度呢?答案是肯定的,参考代码如下:
/* * 枚举中心位置,然后再在该位置上用扩展法,记录并更新得到的最长的回文长度 */ public static int longestPalindrome1(String s) { int evenMaxlength=0; int oddMaxlength = 0; //长度为奇数 for(int i=0;i<s.length();i++) { int j=i-1; int k=i+1; int temp=1; while(j>=0&&k<s.length()&&s.charAt(j)==s.charAt(k)) { temp = temp+2; j--; k++; } oddMaxlength = oddMaxlength>temp?oddMaxlength:temp; } //长度为偶数 for(int i=0;i<s.length();i++) { int j=i; int k=i+1; int temp=0; while(j>=0&&k<s.length()&&s.charAt(j)==s.charAt(k)) { temp = temp+2; j--; k++; } evenMaxlength = evenMaxlength>temp?evenMaxlength:temp; } return oddMaxlength>evenMaxlength?oddMaxlength:evenMaxlength; }
解法二、O(N)解法
在上文的解法一:枚举中心位置中,我们需要特别考虑字符串的长度是奇数还是偶数,所以导致我们在编写代码实现的时候要把奇数和偶数的情况分开编写,是否有一种方法,可以不用管长度是奇数还是偶数,而统一处理呢?比如是否能把所有的情况全部转换为奇数处理?
答案还是肯定的。这就是下面我们将要看到的Manacher算法,且这个算法求最长回文子串的时间复杂度是线性O(N)的。
首先通过在每个字符的两边都插入一个特殊的符号,将所有可能的奇数或偶数长度的回文子串都转换成了奇数长度。比如 abba 变成 #a#b#b#a#, aba变成 #a#b#a#。
/* * 在里面添加特殊字符。添加了“#”,使abba变为a#b#b#a */ public static int longestPalindrome2(String s) { StringBuffer s1 = new StringBuffer(s); int length=s1.length(); for(int i=0,k=1;i<length-1;i++)//给字符串添加 # { s1.insert(k,"#"); k=k+2; } s = s1.toString(); String temp = ""; int max = 0; for(int i=0;i<s.length();i++) { int j=i-1; int k=i+1; int maxtemp = 1; String maxString = Character.toString(s.charAt(i)); while(j>=0&&k<s.length()&&s.charAt(j)==s.charAt(k)) { maxtemp = maxtemp+2; maxString = Character.toString(s.charAt(j))+maxString+Character.toString(s.charAt(k)); j--; k++; } if(max<maxtemp) { max = maxtemp; temp = maxString; } } temp = temp.replaceAll("#", ""); return temp.length(); }