题目描述
给你一个字符串 s,找到 s 中最长的回文子串。
解题思路
这道题看了题解,总的来说有三种解法:动态规划、中心扩散、马拉车算法。第一个动态规划来做这道题不是很划算。包括时间复杂度,空间复杂度,实际上最关键的一点是我觉着这道题没有特别符合动态规划的特性,一般情况下,其实动态规划是不怎么划算的,因为一旦数据量大了之后,使用数组的话空间需求量非常的大,而且动态规划实际上涉及多次重复子问题的计算,如果要避免这个问题还需要用记忆化,也就额外使用一个记忆空间,用空间换时间。最后解决这道题时间复杂度和空间复杂度都比中心扩散法复杂。还有一个最关键的问题我看到这道题第一反应就是两种解法,扩散和双指针,动态规划都没考虑过。后面两种解法中心扩散和马拉车算法实际逻辑是类似的,只是马拉车算法对中心扩散进行了优化。今天只讲解中心扩散法。
我们知道回文串实际上就是一个中心对称字符串,例如:aba关于b中心对称,abba关于bb中心对称。从这两个例子我们也能看出来,偶数个字符串和奇数个字符串对称点是不一样的,奇数个只有一个对称点,而偶数个有两个对称点。题目中要求我们找到一个字符串里的最长回文串,那么我们就可以以每一个字符为对称中心找到以它为对称中心的回文串,然后不断地比较以每个字符为中心的回文串,找到最长的回文串(写到这里,我感觉这方法就是穷举法哈哈哈哈,所以就会出现马拉车算法对它进行改良吧)。这里有一个问题就是上文提到的奇数个字符串和偶数个字符串对称中心是不一样的,这里我们不要去想什么判断啊,没那个必要而且没办法判断啊,你是要已知回文串才能去判断奇数个还是偶数个,我们现在是需要找到这个回文串,直接两种情况都考虑,然后比较两种情况找出来的回文串哪一长就选哪一个,这也是我们最终的目的,就是选出最长的回文串。有一些比较细节的加减一可以画图解决。求回文串函数返回数组也可以改成直接返回回文串字符,然后直接比较返回回文串长度。不过由于String(对象)的特殊性,建议使用StringBuilder或者StringBuffer。
class Solution {
public String longestPalindrome(String s) {
if (s == null || s.length() == 0) return "";
if (s.length() < 2) return s;
int len = s.length();
int left = 0;
int right = 0;
int res[] = new int[2];
for (int i = 0;i < len-1;i++){//考虑偶数个,i不能取最后一个字符
left = i;//向左最开始的扩散点
right = i;//向右最开始的扩散点
//考虑奇偶
int[] odd = centerCount(s,left,right);//奇数一个中心点,中心点就是第i个字符
int[] even = centerCount(s,left,right+1);//偶数两个中心点,中心点是第i个和第i+1个,进入求回文串函数先判断两个中心点是否对称
int max[] = odd[1] > even[1] ? odd:even; //比较偶数个和奇数个谁的回文串更长
res = max[1] > res[1] ? max:res;//比较以每个字符中心的回文串
}
return s.substring(res[0], res[0]+res[1]);//拼接
}
int[] centerCount(String s,int left,int right){
int len = s.length();
while(left >= 0 && right < len){
if(s.charAt(left) == s.charAt(right)){
left--;//由中心向左扩散
right++;//由中心向右扩散
}else{
break;
}
}
//第一个值代表起始位置,加一是因为left的值是第一个不对称的字符的下标;
//第二个值代表回文串的长度,减一是right和left都是第一个不对称的下标,如果减二的话是不包括对称点的
return new int[]{left+1,right-left-1};
}
}