目录
解法1:递归算法
解法2:Map取同字母位置法
解法3:中心扩展法
解法4:动态规划法
解法5: Manacher算法
示例 1:
输入:s = "babad" 输出:"bab" 解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd" 输出:"bb"
提示:
1 <= s.length <= 1000
s
仅由数字和英文字母组成
解法1:递归算法
1)判断输入的字符串是否是回文,若是肯定为最大回文串。或者字符串长度<=1 直接返回。(结束条件)
2) 拆分问题降级。
若字符串不是回文,那么只能在s[0,n-2] 和 s[1,n-1]两个子串里去找最大回文串。
然后选取长度更大的那个返回结果即是结果。
方法很简单,代码如下:
class Solution {public String longestPalindrome(String s) {if (isPalindrome(s)) {return s;}String left, right;int n = s.length();// find palindrome in left partleft = longestPalindrome(s.substring(0, n-1));// find palindrome in right partright = longestPalindrome(s.substring(1, n)); if (left.length() > right.length()) {return left;} else {return right;}}public boolean isPalindrome(String s) {char[] schars = s.toCharArray();int n = schars.length;for (int i=0; i<n; i++) {if (schars[i] != schars[n-1-i]) {return false;}}return true;}
}
前面都能跑过,碰到测试用例:"babaddtattarrattatddetartrateedredividerb" 运行超时。
这个递归,很多重复计算,效率比较低。那就优化下,来个高效率点的吧。
解法2:Map取同字母位置法
我们知道每一个回文串,它的开头和结尾两个字母肯定相同。那么我们可以得到一个灵感,把所有字母的字符串中位置记录下来,回文只可能在那些出现了两次以上位置的字母里出现。
例如:"abcabcde"
Map [Character, List<Integer>]
a => [0, 3]
b => [1, 4]
c => [2, 5]
d => [6]
e => [7]
那么我们从字母的位移列表中,选出超过2个的进行检查所有的字符串是否为回文,然后取出最大的就可以了。
代码如下:
class Solution {public String longestPalindrome(String s) {if (isPalindrome(s)) {return s;}// letter <-> count & index listMap<Character, List<Integer>> letterMap = new HashMap<>();char[] schars = s.toCharArray();for (int i=0; i<schars.length; i++) {Character c = Character.valueOf(schars[i]);List<Integer> indexs = letterMap.get(c);if (indexs != null) {indexs.add(Integer.valueOf(i));} else {indexs = new LinkedList<>();indexs.add(Integer.valueOf(i));letterMap.put(c, indexs);}}String maxRome = "" +schars[0];for (Map.Entry<Character,List<Integer>> entry: letterMap.entrySet()) {// more than one index position, maybe there is a palindromeList<Integer> indexList = entry.getValue();int indexSize = indexList.size();if (indexSize > 1) {for (int i=0; i<indexSize; i++) {for (int j=indexSize-1; j>i; j--) {int start = indexList.get(i);int end = indexList.get(j) + 1;// sub-string size is less than maxRome, skip itif (end-start <= maxRome.length()) {continue;} else {String subStr = s.substring(start,end);if (isPalindrome(subStr) && subStr.length() > maxRome.length()) {maxRome = subStr;}}}}}}return maxRome;}public boolean isPalindrome(String s) {int n = s.length();for (int i=0; i<n; i++) {if (s.charAt(i) != s.charAt(n-1-i)) {return false;}}return true;}}
运行效果:
虽然过了,但是5%的分布,效果不怎么样。稍微优化下。
1)代码中用的LinkedList来进行记录相同字母的位移下标,在频繁get的时候效率不如ArrayList高。
2)字母Map初始化的时候没有给定初始值,那么会导致resize占用时间。那么我们给定一个size*4/3作为它的初始值。
优化结果代码如下:
class Solution {public String longestPalindrome(String s) {char[] schars = s.toCharArray();if (isPalindrome(schars, 0, s.length()-1)) {return s;}// letter <-> count & index listMap<Character, List<Integer>> letterMap = new HashMap<>(s.length()*4/3);for (int i=0; i<schars.length; i++) {Character c = Character.valueOf(schars[i]);List<Integer> indexs = letterMap.get(c);if (indexs != null) {indexs.add(Integer.valueOf(i));} else {indexs = new ArrayList<>();indexs.add(Integer.valueOf(i));letterMap.put(c, indexs);}}String maxRome = "" +schars[0];for (Map.Entry<Character,List<Integer>> entry: letterMap.entrySet()) {// more than one index position, maybe there is a palindromeList<Integer> indexList = entry.getValue();int indexSize = indexList.size();if (indexSize > 1) {for (int i=0; i<indexSize; i++) {for (int j=indexSize-1; j>i; j--) {int start = indexList.get(i);int end = indexList.get(j);int subStrLen = end-start+1;// sub-string size is less than maxRome, skip itif (subStrLen <= maxRome.length()) {break;} else {String subStr = s.substring(start,end+1);if (isPalindrome(schars, start, end)) {maxRome = s.copyValueOf(schars, start, subStrLen);}}}}}}return maxRome;}public boolean isPalindrome(char[] chars, int start, int end) {while(start < end) {if (chars[start++] != chars[end--]) {return false;}}return true;}}
经过上面两个代码优化后,执行效果如下:
执行结果位于57%位置,还凑合吧。
解法3:中心扩展法
实现代码如下:
class Solution {public String longestPalindrome(String s) {int length = s.length();if (length < 2) {return s;}char[] schars = s.toCharArray();String maxRome = "" +schars[0];// search max palindrome from the middle point schars[i]// palindrome middle from (0, len-1]for (int i=1; i<length; i++) {// find max even palindrome from middle pointmaxRome = search(schars, i-1, i, maxRome);// find max odd palindrome from middle pointmaxRome = search(schars, i-1, i+1, maxRome);}return maxRome;}public String search(char[] schars, int left, int right, String maxRome) {int length = schars.length;while(left >= 0 && right<length && schars[left] == schars[right]) {left--;right++;}if (right - left -1 > maxRome.length()) {return String.copyValueOf(schars, left + 1, right - left -1);}return maxRome;}}
执行效果如下
93.2%分位,已经很好了。强烈推荐解法。
解法4:动态规划法
理解起来稍微复杂点,可以作为学习动态规划算法的例子。
实现代码如下:
class Solution {public String longestPalindrome(String s) {int length = s.length();if (length < 2) {return s;}// palindrome[i][j] = true => s[i,j] is palindromeboolean[][] palindrome = new boolean[length][length];// len = 1, all is palindromefor (int i=0; i<length; i++) {palindrome[i][i] = true;}int start = 0, maxLen = 1;char[] schars = s.toCharArray();// palindrome[i][j] = palindrome[i+1][j-1] && s[i]==s[j]for (int len = 2; len<=length; len++) {for (int i=0; i<length; i++) {// j - i + 1 = lenint j = i + len -1;if (j>length-1) {break;}if (schars[i] == schars[j]) {if (len <= 3) {palindrome[i][j] = true;} else {palindrome[i][j] = palindrome[i+1][j-1];}} else {palindrome[i][j] = false;}if (palindrome[i][j] && len > maxLen) {start = i;maxLen = len;}}}return String.copyValueOf(schars, start, maxLen);}
}
运行效果如下:
19.07%还凑合吧。
解法5: Manacher算法
还有个Manacher算法比较复杂,效果也没见得比"中心扩展法"好太多,就不推荐了。
感兴趣的童鞋可以去这里看
. - 力扣(LeetCode)