public String longestPalindrome(String s){ String ans=""; int len=0; for (int i=0;i<s.length();i++){ for (int j=i+1;j<s.length();j++){ if (isPalindrome(s.substring(i,j))&&len<j-i+1){ ans=s.substring(i,j); len=Math.max(len,ans.length()); } } } return ans; }
booleanisPalindrome(String s){ for (int i=0;i<s.length()/2;i++){ if (s.charAt(i)!=s.charAt(s.length()-1-i)) returnfalse; } returntrue; }
public String longestPalindrome(String s){ // 如果是空串,则直接返回空串表示没有回文串 if (s==null||s.length()<1)return""; int start=0,end=0; for (int i=0;i<s.length();i++){ // 判断i为中心的基数回文长度 int len1=expandAroundCenter(s,i,i); // 判断i,i+1为中心的偶数回文长度 int len2=expandAroundCenter(s,i,i+1); int len=Math.max(len1,len2); // 如果新回文串的长度大于之前的回文串长度,则更新 if (len>end-start){ // start=i-(len-1)/2; end=i+len/2; } } // 因为substring()方法截取的范围是[起始索引,结束索引),所以第二个参数需要+1 return s.substring(start,end+1); }
这一步是马拉车算法的精髓所在,充分利用的回文的对称性。用c表示回文子串的中心,用r表示回文子串的右边半径。所以r=c+p[i]。C 和 R 所对应的回文串是当前循环中 R 最靠右的回文串,而不一定是最长的回文串。
用 i_mirror 表示当前需要求的第 i 个字符关于 C 对应的下标。 我们现在要求 P [ i ],如果是用中心扩展法,那就向两边扩展比对就行了。但是我们其实可以利用回文串中心 C 的对称性。i 关于 C 的对称点是 i_mirror,P [ i_mirror ] = 3,所以 P [ i ] 也等于 3。
但是有三种情况将会造成直接赋值为 P [ i_mirror ] 是不正确的,下边一一讨论:
情况一:超出了 R
当我们要求 P [ i ] 的时候,P [ mirror ] = 7,而此时 P [ i ] 并不等于 7,为什么呢,因为我们从 i 开始往后数 7 个,等于 22,已经超过了最右的 R,此时不能利用对称性了,但我们一定可以扩展到 R 的,所以 P [ i ] 至少等于 R - i = 20 - 15 = 5,会不会更大呢,我们只需要比较 T [ R+1 ] 和 T [ R+1 ]关于 i 的对称点就行了,就像中心扩展法一样一个个扩展。
情况二:P [ i_mirror ] 遇到了原字符串的左边界
此时P [ i_mirror ] = 1,但是 P [ i ] 赋值成 1 是不正确的,出现这种情况的原因是 P [ i_mirror ] 在扩展的时候首先是 “#” == “#”,之后遇到了 “^” 和另一个字符比较,也就是到了边界,才终止循环的。而 P [ i ] 并没有遇到边界,所以我们可以继续通过中心扩展法一步一步向两边扩展就行了。
情况三:i 等于了 R
此时我们先把 P [ i ] 赋值为 0,然后通过中心扩展法一步一步扩展就行了。
考虑 C 和 R 的更新 就这样一步一步的求出每个 P [ i ],当求出的 P [ i ] 的右边界大于当前的 R 时,我们就需要更新 C 和 R 为当前的回文串了。因为我们必须保证 i 在 R 里面,所以一旦有更右边的 R 就要更新 R。
此时的 P [ i ] 求出来将会是 3,P [ i ] 对应的右边界将是 10 + 3 = 13,所以大于当前的 R,我们需要把 C 更新成 i 的值,也就是 10,R 更新成 13。继续下边的循环。
public String longestPalindrome(String s){ // 获取扩充后的字符串T String T=preProsess(s); int len=T.length(); int[] p=newint[len]; int c=0,r=0; // 不需要判断前后边界字符“^"和“$”,所以循环范围是[1,len-1) for (int i=1;i<len-1;i++){ // 第i个字符关于c对称的下标 int i_mirror=2*c-i; if (r>i){//如果i小于对称半径r p[i]=Math.max(r-i,p[i_mirror]); }else { p[i]=0; }
// 遇到三种特殊情况时,需要退化到中心扩展法 while (T.charAt(i+1+p[i])==T.charAt(i-1-p[i])){ p[i]++; }
// 判断是否需要更新c和r if (i+p[i]>r){ c=i; r=p[i]; } } // 找出p[]数组中最大的值 int currentIndex=0,maxLen=0; for (int i=0;i<p.length;i++){ if (p[i]>maxLen){ currentIndex=i; maxLen=p[i]; } }
// 求子串首字符在原字符串中的下标 int start=(currentIndex-maxLen)/2; return s.substring(start,start+maxLen); } // 扩充字符串 public String preProsess(String s){ if (s.equals(""))return"$"; String result="^"; for (int i=0;i<s.length();i++){ result+="#"+s.charAt(i); } result+="#$"; return result; }