题目要求

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

Example:

Input: "babad"

Output: "bab"

Note: "aba" is also a valid answer.

Example:

Input: "cbbd"

Output: "bb"

翻译过来就是说:在一个字符串中寻找最长的子字符串,该字符串是回数(即从左往右和从右往左读的结果是相同的)。该字符串的最大长度为1000

思路一:头指针+尾指针

遍历字符串,找到当前头指针的元素下一次出现时的下标,并且判断该子字符串是否是回数。

    public String longestPalindrome(String s) {
            StringBuilder result = new StringBuilder();
            int resultLength = 0;
            
            StringBuilder temp = new StringBuilder();
            for(int i = 0 ; i<s.length()-resultLength ; i++){
                
                for(int j = s.lastIndexOf(s.charAt(i)) ; j>=i+resultLength ; j = s.substring(0, j).lastIndexOf(s.charAt(i))){
                    temp = new StringBuilder(s.substring(i, j+1));
                    if(temp.toString().equals(temp.reverse().toString())){
                        result = temp;
                        resultLength = temp.length();
                        break;
                    }    
                }
            }
            return result.toString();
        }

在该方法中,已经对遍历进行了优化,尽可能减少了无效遍历,例如当长度一定小于当前结果的最大长度时,跳出当前循环并进入下一个遍历。但是仍然有很多无效的遍历,因此该答案最后还是超时的。

思路二:指针+最大长度

现在我们从回数的特点入手。
假若一个字符串是一个回数,那么该字符串内部一定还存在更多的回数。例如,abbbcbbba是一个回数,那么bbbcbbb一定是一个回数,那么bcb也是回数,最后到b。同理,bccb是一个回数,那么cc也是一个回数。因此可以看出,假设当前一个字符串是回数,那么加上两侧的字符可能还是回数。假设当前一个字符串不是回数,那么加上右侧的字符可能构成一个回数。
因此,假设当前得到的回数的最大长度为n,我们可以判断n+1或者n+2是不是回数。
为什么这么判断呢?下面给出证明。
我们假设有一个字符串xxxxxxxxabaxxxxxxs,其中x代表任意字符。
假设此时指针指向s,而已知最大回数子字符串的长度为3。我们只需要判断xxxs以及xxxxs是不是回数。无需判断xxs乃至更近是因为它们的长度必然无法超过当前的最大长度。而无需判断xxxxxs乃至更远是因为假如xxxxxs是回数,那么xxxx一定是回数,则当前的最大长度为4而不是3,与题设不符。所以只需判断两种情况。
这里就充分利用了回数的性质,省去了很多无效的遍历

    public String longestPalindrome2(String s) {
        StringBuilder result = new StringBuilder();
        int curLength = 0;
        
        for(int i = 0 ; i<s.length() ; i++){
            if(isPalindromic(s, i-curLength-1, i)){
                result = new StringBuilder(s.substring(i-curLength-1, i+1));
                curLength += 2;
            }else if(isPalindromic(s, i-curLength, i)){
                result = new StringBuilder(s.substring(i-curLength, i+1));
                curLength += 1;
            }
        }
        return result.toString();
    }
    public boolean isPalindromic(String s, int start, int end){
        if(start<0){
            return false;
        }
        while(start<end){
            if(s.charAt(start++)!=s.charAt(end--)) return false; 
        }
        return true;
    }

思路三:由中间至两边找回数

思路三就像是思路一的彻底反转。思路一优先寻找回数的边缘,而思路三则直接从中间开始寻找,直至找到最远的边缘。
正如上面所说,回数的中间可能是一个单值,如aba中的b;也可能是双值,如abba中的bb。
我们可以对当前下标上的单值双值都进行尝试
以下是我在leetcode上找到的一个实现(88%)

public class Solution {
private int lo, maxLen;

public String longestPalindrome(String s) {
    int len = s.length();
    if (len < 2)
        return s;
    
    for (int i = 0; i < len-1; i++) {
         extendPalindrome(s, i, i);  //assume odd length, try to extend Palindrome as possible
         extendPalindrome(s, i, i+1); //assume even length.
    }
    return s.substring(lo, lo + maxLen);
}

private void extendPalindrome(String s, int j, int k) {
    while (j >= 0 && k < s.length() && s.charAt(j) == s.charAt(k)) {
        j--;
        k++;
    }
    if (maxLen < k - j - 1) {
        lo = j + 1;
        maxLen = k - j - 1;
    }
}}

clipboard.png
想要了解更多开发技术,面试教程以及互联网公司内推,欢迎关注我的微信公众号!将会不定期的发放福利哦~


raledong
2.7k 声望2k 粉丝

心怀远方,负重前行