[LeetCode] 005 - Longest Palindrome Substring (Detailed)

Keywords: Java Programming Python less

Topic Description

Given a string s, find the longest palindrome substring in S. You can assume that the maximum length of S is 1000.

Example 1:

  • Input: "babad"
  • Output: "bab"

Note that "aba" is also an effective answer.

Example 2:

  • Input: "cbbd"
  • Output: "bb"

Thoughts on Problem Solving

1. Violent resolution

The easiest thing to think of is the violent solution. Find out each substring of the original string s, and then judge whether it is palindrome or not, and find the longest one.

class Solution {
	//Determine whether a string is palindrome
    public static boolean judge(String s){
        StringBuffer sb = new StringBuffer(s);
        sb.reverse();
        String str = new String(sb);
        if(s.equals(str)){
            return true;
        }
        return false;
    }
    public String longestPalindrome(String s){
        int l = s.length();
        //If the length of a string is less than 2, the string is returned.
        if(l < 2){
            return s;
        }
        //Get each substring through two for loops and decide whether it is palindrome or not
        //Here, from the longest substring to the shortest substring, so when the first substring is palindrome, it is the longest palindrome substring.
        for(int i = l; i > 0; i--){
            for(int j = 0; j <= l - i; j++){
                String str = s.substring(j, j + i);
                if(judge(str)){
                    return str;
                }
            }
        }
        return s;
    }
}

The time complexity of each substring is O(N2)O(N^2)O(N2), and the time complexity of judging whether the substring is palindrome is O(N)O(N)O(N), so the time complexity of violent solution is O(N3)O(N^3)O(N3). The complexity of this algorithm is too high to satisfy the interviewer.

2. Dynamic programming


_For string SSS, suppose that f [i] [j] f [i] [j] f [i] [i] [j] f [i] [j] indicates whether the substring of string SSS subscripts from iii to jjj is a palindrome substring.

  • I f f [i] [j] f [i] [j] f [i] [i] [j] f [i] [j] is palindrome, then f[i+1][j_1]f[i+1][j-1]f[i+1][j_1] must be palindrome substring
  • I f f[i+1][j 1]f[i+1][j-1]f[i+1][j 1] is not palindrome, then f[i][j]f[i][j]f[i][j] f [i] [j] must not be palindrome substring

The longest palindrome substring can be decomposed into a series of sub-problems, which can be solved by dynamic programming. From this we can get the recursive equation of dynamic programming:

f[i][j]={f[i+1][j_1],S[i]=S[j]false,S[i] is not equal to S [j] f [i] [j]= begin {cases}. f[i+1][j-1] &amp;,S[i]=S[j] \\ False & amp;, S [i] is not equal to S[j].\ \ end{cases}f[i][j]={f[i+1][j_1]false, S[i]=S[j],S[i] is not equal to S[j]

Initial state

{f[i][i]=truef[i][i+1]=true if S[i]==S[i+1]\begin{cases} f[i][i]=true \\ f[i][i+1]=true \ if \ S[i]==S[i+1]\\ \end{cases}{f[i][i]=truef[i][i+1]=true if S[i]==S[i+1]​

  • F [i] [i] = truef [i] [i] = truef [i] [i] = truef [i] [i] = true denotes a single character as a palindrome
  • f[i][i+1]=true if S[i]==S[i+1] f[i][i+1]=true if\ S [i]= S [i + 1]= true [i + 1] f[i][i+1]=true if S [i]= S [i + 1] means that two connected characters are palindromes

C++ code:

class Solution {
public:
    string longestPalindrome(string s)
    {
        //If the string is empty, return empty
        if (s.empty()) return "";
        //String length
        int len = s.size();
        //Single character
        if (len == 1) return s;
        //Keep the longest palindrome substring length
        int longest = 1;
        //Keep the Starting Point of the Longest Palindrome Substring
        int start=0;
        //A vector container is defined with the element type vector < int > initialized to contain len vector < int > objects.
        //Each object is a copy of a newly created vector < int > object, and the newly created vector < int > object is initialized to contain len 0.
        //Similar to creating a two-dimensional array of lenxlen, elements can be accessed by dp[i][j].
        vector<vector<int>> dp(len,vector<int>(len));
        //initial condition
        for (int i = 0; i < len; i++)
        {
            //Two-dimensional array diagonals are single characters, all of which are 1
            dp[i][i] = 1;
            if(i<len-1)
            {
                if (s[i] == s[i + 1])//Two contiguous characters are identical
                {
                    dp[i][i + 1] = 1;//Palindrome, marked 1
                    start=i;//Change the starting position
                    longest=2;//The longest palindrome substring length is 2
                }
            }
        }
        //String Length >= 3
        for (int l = 3; l <= len; l++)//Traversing the length of each seed string
        {
            for (int i = 0; i+l-1 < len; i++)//Enumeration of the Starting Point of Substring
            {
                int j=l+i-1;//End
                if (s[i] == s[j] && dp[i+1][j-1]==1) //If the characters at both ends of the substring are the same and the substrings that remove the characters at both ends are palindrome substrings
                {
                    dp[i][j] = 1;//At this point, the substring is palindrome, marked 1
                    start=i;//Update Start Location
                    longest = l;//Update Maximum Length
                }
            }
        }
        return s.substr(start,longest);//start starts in the interception string and intercepts longest characters
    }
};

Java code:

class Solution {
    public String longestPalindrome(String s) {
        if("".equals(s)){
            return "";
        }
        
        int len = s.length();
        if(len == 1){
            return s;
        }
        int sLength = 1;
        int start = 0;
        int[][] dp = new int[len][len];
        for(int i = 0; i < len; i++){
            dp[i][i] = 1;
            if(i < len - 1 && s.charAt(i) == s.charAt(i+1)){
                dp[i][i+1] = 1;
                sLength = 2;
                start = i;
            }
        }
        for(int l = 3; l <= len; l++){
            for(int i = 0; i + l -1 < len; i++){
                int j = i + l - 1;
                if(s.charAt(i) == s.charAt(j) && dp[i+1][j-1] == 1){
                    dp[i][j] = 1;
                    start = i;
                    sLength = l;
                }
            }
        }
        return s.substring(start,start+sLength);
    }
}

Python 3 code:

class Solution:
    def longestPalindrome(self, s: str) -> str:
        length = len(s)  # Calculate string length
        if length == 0:
            return ""
        if length == 1:
            return s
        
        sLength = 1
        start = 0
        dp = [[0 for i in range(length)] for j in range(length)]  # Create a two-dimensional array initialized to 0
        for i in range(0, length):
            dp[i][i] = 1
            if i < length - 1 and s[i] == s[i+1]:
                dp[i][i+1] = 1
                sLength = 2
                start = i

        for l in range(3, length+1):
            for i in range(0, length-l+1):
                j = i + l - 1
                if s[i] == s[j] and dp[i+1][j-1] == 1:
                    dp[i][j] = 1
                    start = i
                    sLength = l
                    
        return s[start : start+sLength]

3. Central Expansion Method

The idea of_central expansion is that when traversing to an element of an array, it expands to both sides with this element as the center, and if the elements on both sides are the same, it continues to expand, otherwise it stops expanding. The complexity of the algorithm is O(N2)O(N^2)O(N2).

The following figure: When traversing to 3:00

However, single character expansion is flawed when the length of the string is even, such as 1221.

1, 2, 2, 1 is a palindrome string, but no symmetric center can be found, which makes it difficult to extend from one element to both sides.

  • 1. Expanding around a single character and two adjacent characters respectively (the following code uses this method)
  • 2. Fill in 1, 2, 2, 1. For example, fill in with # to get:, 1,, 2,, 2,, 1,#

Java code:

class Solution {
    public String longestPalindrome(String s) {
        
        if(s == null || s.length() < 1)
            return "";
        int start = 0;
        int end = 0;
        //The central expansion method traverses the central points in turn
        for(int i = 0; i < s.length(); i++){
            //Finding the Length of Expansion Center
            int len1 = expandLen(s, i, i);  //Centering on each character
            int len2 = expandLen(s, i, i+1);  //With each adjacent two characters as the center
            int len = Math.max(len1, len2);
            if(len > end - start){
                start = i - (len - 1) / 2;
                end = i + len / 2;
            }
        }
        return s.substring(start, end+1);
        
    }
    
    public int expandLen(String s, int L, int R){
        int left = L;
        int right = R;
        while(left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)){
            left--;
            right++;
        }
        return right - left - 1;
    }
}


C++ code:

class Solution {
public:
    string longestPalindrome(string s) {
        int len=s.size();
        if(len==0||len==1)
            return s;
        int start=0;//Record the starting position of palindrome substring
        int end=0;//Record palindrome substring termination position
        int mlen=0;//Recording the Length of the Maximum Palindrome Substring
        for(int i=0;i<len;i++)
        {
            int len1=expendaroundcenter(s,i,i);
            int len2=expendaroundcenter(s,i,i+1);
            mlen=max(max(len1,len2),mlen);
            if(mlen>end-start+1)
            {
                start=i-(mlen-1)/2;
                end=i+mlen/2;
            }
        }
        return s.substr(start,mlen);
        //This function means to get a string of mlen length from start
    }
private:
    int expendaroundcenter(string s,int left,int right)
    //Calculating the length of palindrome string centered on left and right
    {
        int L=left;
        int R=right;
        while(L>=0 && R<s.length() && s[R]==s[L])
        {
            L--;
            R++;
        }
        return R-L-1;
    }
};

Python 3 code:

class Solution:
    def longestPalindrome(self, s: str) -> str:
        def expand(s, left, right):
            while left >= 0 and right < len(s) and s[left] == s[right]:
                left -= 1
                right += 1
            return right - left - 1
        
        start = 0
        end = 0
        for i in range(len(s)):
            len1 = expand(s, i, i)
            len2 = expand(s, i, i+1)
            max_len = max(len1, len2)
            if max_len > end - start:
                start = i - int((max_len-1)/2)
                end = i + int(max_len/2)
        return s[start : end+1]

Manacher algorithm

Direct examples are given to illustrate:

The core idea of Manacher algorithm is to use palindrome substrings generated during previous traversals.

1. Principle

As shown above:

  • idxidxidx represents the symmetric axis of the blue palindrome substring (known)
  • Now find the palindrome substring with curcurcur as the symmetric axis (unknown)
  • Preprepreprep is the symmetric axis with idxidx and the symmetric position of curcurcur (traversal to curcurcur, prepreprepreprep is known)

Scenario 1: The palindrome substrings of I'I &#x27; i'exceed the left boundary of the palindrome substrings of idxidxidx

Given that idxidxidx is the central axis of the blue block substring, we now seek the palindrome substring with i as the central axis.

  • I I I is in the palindrome substring with idxidx as its central axis, I'I & X 27; i'is iii's symmetric point about idxidx, and the palindrome substring with I'I & X 27; i'as its central axis is known (orange block).
  • Where I I I points to ccc, I'I & x 27; i'points to bbb, idxidxidx points to eee (lowercase characters are variables, capital letters are specific characters)
  • So from the eee-centered palindrome substring, we know that b=cb=cb=c
  • Because the palindromes of idxidxidx do not include a a A and D D d, a! = da! = da! = D (when a=da=da=d, the palindrome substrings of idxidx will be extended)
  • Because IDX IDX IDX left to bbb and idxidx right to c c c are equal, and a!=da!=da!=d, the palindrome radius centered on ccc is only IDX right location(c)idx right-location(c)idx right location(c).
  • If a a a's symmetry point about bbb-centered palindromes is a'a & # x27; a'. The symmetry point of a'a & # x27; A'palindrome centered on eee is a''a & # x27; & # x27; & # x27; a', then a=a'= a'= a'!= Da = A & # x27; = A & # x27; & # x27; = Da = a'= a'!= d

Examples are given to illustrate:

Because there are even and odd string lengths, we use # to fill in, as follows:___________.

  • When traversing to 13B, the palindrome substrings with 9D as the central axis are from 2 to 16 (as previously traversed, known), and the length is 16_2+1=1516-2+1=1516_2+1=1516_2+1=15.
  • The palindrome substring centered on No. 5 B is from No. 0 to No. 10 (known), and its length is 10_0+1=1110-0+1=1110_0+1=11.
  • The symmetric point of No. 13 B about No. 9D is No. 5 B, and now the palindrome substring with No. 13 B as the symmetric axis is required.


No. 1 D and No. 17E are not equal. Now, as long as the disc decides whether the palindrome substring centered on No. 13 B contains No. 17 or not.

  • If 17 E is included, then its point of symmetry for 13 B is 9D, and the point of symmetry for 9D for 5 B is 1D.
  • According to the symmetry, No. 17 E should be equal to No. 9 D and No. 1 D, which is obviously different.


Therefore, the palindrome substrings with No. 13 B as the central axis do not include No. 17 E. According to the palindrome substrings with No. 5 B and No. 9 D as the central axis, we can see that:

  • No. 2 # to No. 5 B equals No. 8 # to No. 5 B
  • No. 10 to No. 13B equals No. 16 to No. 13B

Case 2: The palindrome substring idxidxidx of I'I & x 27; i'contains the palindrome substring

The largest palindrome substring of known I I i's symmetric point I'I & x 27; i's on idxidx's central axis is shown in the figure above.

  • Because the palindrome substrings of I'I & x 27; i'do not include a,ba,ba,b, then a!=ba!=ba!=b
  • And because a,da,da,d and b,cb,cb,c are symmetric about idxidx, remember b=c,a=db=c,a=db=c,a=d, so c!=dc!=dc!=d
  • Because the character between ccc and ddd is palindrome, because the character between ccc and ddd is symmetrical between aaa and bbb of idxidx, and between aaa and bbb is palindrome string, so the character between ccc and ddd is palindrome string. So the length of the palindrome substring of i I i is the same as that of i'I & X 27; i'

Examples are given to illustrate:

Case 3: The left boundary of the palindrome substring of I'I & x 27; i'coincides with the left boundary of the palindrome substring of idxidx

From the palindrome substring centered on idxidx, it can be seen that b=c,a!=db=c,a!=db=c,a!=d, and the palindrome length of I'I & # x 27; i'is between aaa and bbb (excluding a,ba,ba,b).

So the length of the palindrome substring of iii is at least as shown in the figure above.

  • If c=dc=dc=d, the palindrome about iii as the central axis can be extended
  • If c!=dc!=dc!=d, it happens to be shown in the figure above.

Examples (c=d):

I I i's longest palindrome substring of I'I & X 27; i's symmetry point about idxidx is shown above, and the left boundary of I'I & X 27; i's palindrome coincides with idxidxidx, so iii-centered palindromes need to be expanded from the blue box boundary to the left and right sides.

Case 4: I'I & x27; i's palindrome substring is not included in idxidxidx's palindrome substring

At this point, we do not have any information to use, can only take iii as the central axis, extending to the left and right sides. Find out its longest palindrome substring.

2. C++ Code

class Solution {
public:
    string longestPalindrome(string s) {
        //Calculate string length
        int n = s.size();
        if(n <= 1) return s;
        //Generate a string str containing 2*n+1 character 0
        string str(2*n+1,'0');
        bool flag = 1;
        int j = 0;
        int maxIdx = 0;
        //Fill in the character and get a string like # 1 # 2 # 3 # 2 # 1.
        for(int i=0; i<2*n+1; i++){
           if(flag){
               str[i]='#';
               flag = false;
           }else{
               str[i] = s[j++];
               flag = true;
           }
        }
        //Represents the radius of palindrome substrings with i as the central axis and does not contain the symmetric axis
        //For example, abcdcba, the subscript of d is 4, radius[4] = 3, radius[0] = 0
        vector<int> radius(2*n+1,0); 
        int idx = 0; //A central axis subscript representing the last palindrome substring
        int rad = 1; //idx can contain the next character subscript in the largest range
        for(int i=1; i<2*n+1; i++){
            //Situation IV
            if(i >= rad){
                forceExtend(str, radius, idx, rad, i);
                maxIdx = (radius[i] > radius[maxIdx] ? i : maxIdx);
            }else if(i < rad){
                int j = 2*idx - i; //i On the Symmetric Point j of idx
                int idx_radius = idx - radius[idx]; //Left boundary subscripts of idx palindrome substrings
                int j_radius = j - radius[j];//The left boundary subscript of the palindrome substring of j
                if(j_radius > idx_radius){ //Situation II
                    radius[i] = radius[j];  //i's palindrome substring is the same length as its palindrome substring about idx symmetry points
                }else if(j_radius < idx_radius){//Situation I
                    radius[i] = idx + radius[idx] - i;//Right boundary subscript-i subscript of idx
                }else{ //Situation III
                    radius[i] = idx + radius[idx] - i;//at least
                    int count = 1;
                    //When equal, continue to expand
                    while((i + radius[i] + count) <= str.size()
                          && (i - radius[i] - count) >= 0
                          && str[i + radius[i] + count] == str[i - radius[i] - count]){
                        count++;
                    }
                    //Unequal time
                    radius[i] += (count - 1);
                    //Update the next character subscript at the center and right boundary of the longest palindrome substring
                    if(i + radius[i] >= rad){
                        idx = i;
                        rad = i + count;
                    }
                }
                //Update the center of the longest palindrome substring
                maxIdx = (radius[i] > radius[maxIdx] ? i : maxIdx);
            }
            
        }
        
        string ret = getMaxSubString(str, maxIdx, radius[maxIdx]);
        return ret;
    }
    //Situation IV
    void forceExtend(const string& str, vector<int>& radius, int &idx, int &rad, const int k){
        int count = 1;
        while((k - count) >=0 
              && (k + count) < str.size() 
              && str[k - count] == str[k + count]){
            count++;
        }
        radius[k] = count - 1;
        if((k + radius[k]) >= rad){
            idx = k;
            rad = k + count;
        }
    }
    //Find the longest palindrome substring
    string getMaxSubString(const string &str,const int k,const int r){
        string ret(r, '0');
        int j = 0;
        for(int i = k-r+1; i <= k+r; i+=2){
            ret[j++] = str[i];
        }
        return ret;
    }
    
};

3. Java code

public class Solution {
    public static String longestPalindrome(String s) {
        int n = s.length();
        if (n <= 1) return s ;
        
        StringBuilder strb = new StringBuilder();
        strb.append("#");
        for (int i = 0; i < s.length(); i++) {
            strb.append(s.charAt(i));
            strb.append("#");
        }
        
        int len = strb.length();
        int[] radius = new int[len];
        int idx = 0; //A central axis subscript representing the last palindrome substring
        int rad = 1; //idx can contain the next character subscript in the largest range
        int j = 0;
        int maxIdx = 0;
        for (int i = 1; i < len; i++) {
            //Situation IV
            if(i >= rad){
                int count = 1;
                while((i - count) >=0 
                      && (i + count) < strb.length()
                      && strb.charAt(i - count) == strb.charAt(i + count)){
                    count++;
                }
                radius[i] = count - 1;
                if((i + radius[i]) >= rad){
                    idx = i;
                    rad = i + count;
                }
                maxIdx = (radius[i] > radius[maxIdx] ? i : maxIdx);
            }else if(i < rad){
                j = 2*idx - i; //i On the Symmetric Point j of idx
                int idx_radius = idx - radius[idx]; //Left boundary subscripts of idx palindrome substrings
                int j_radius = j - radius[j];//The left boundary subscript of the palindrome substring of j
                if(j_radius > idx_radius){ //Situation II
                    radius[i] = radius[j];  //i's palindrome substring is the same length as its palindrome substring about idx symmetry points
                }else if(j_radius < idx_radius){//Situation I
                    radius[i] = idx + radius[idx] - i;//Right boundary subscript-i subscript of idx
                }else{ //Situation III
                    radius[i] = idx + radius[idx] - i;//at least
                    int count2 = 1;
                    //When equal, continue to expand
                    while((i + radius[i] + count2) < len
                          && (i - radius[i] - count2) >= 0
                          && strb.charAt(i + radius[i] + count2) == strb.charAt(i - radius[i] - count2)){
                        count2++;
                    }
                    //Unequal time
                    radius[i] += (count2 - 1);
                    //Update the next character subscript at the center and right boundary of the longest palindrome substring
                    if(i + radius[i] >= rad){
                        idx = i;
                        rad = i + count2;
                    }
                }
                //Update the center of the longest palindrome substring
                maxIdx = (radius[i] > radius[maxIdx] ? i : maxIdx);
            }
        }        
        StringBuilder ret = new StringBuilder();
        for(int i = maxIdx-radius[maxIdx]+1; i <= maxIdx + radius[maxIdx]; i+=2){
            ret.append(strb.charAt(i));
        }
        return ret.toString();
    }
}

4. Python 3 Code

class Solution:
    def longestPalindrome(self, s: str) -> str:
        n = len(s)
        if n <= 1:
            return s
        st = '#' + '#'.join(s) + '#'
        sLen = len(st)
        radius = [0] * sLen
        idx = 0
        rad = 1
        maxIdx = 0
        for i in range(1, sLen):
            if i >= rad:
                count = 1
                while (i - count) >= 0 and (i + count) < sLen and st[i - count] == st[i + count]:
                    count += 1
                radius[i] = count - 1
                if (i + radius[i]) >= rad:
                    idx = i
                    rad = i + count
                maxIdx = i if (radius[i] > radius[maxIdx]) else maxIdx
            elif i < rad:
                j = 2 * idx - i
                idx_radius = idx - radius[idx]
                j_radius = j - radius[j]
                if j_radius > idx_radius:
                    radius[i] = radius[j]
                elif j_radius < idx_radius:
                    radius[i] = idx + radius[idx] - i
                else:
                    radius[i] = idx + radius[idx] - i
                    count2 = 1
                    while (i + radius[i] + count2) < sLen and (i - radius[i] - count2) >= 0 and st[i + radius[i] + count2] == st[i - radius[i] - count2]:
                        count2 += 1
                    radius[i] += (count2 - 1)
                    if (i + radius[i]) >= rad:
                        idx = i
                        rad = i + count2
                maxIdx = i if (radius[i] > radius[maxIdx]) else maxIdx

        ret = []
        for i in range(maxIdx-radius[maxIdx]+1, maxIdx + radius[maxIdx]+1, 2):
            ret.append(st[i])
        return ''.join(ret)

Posted by hessian on Wed, 24 Jul 2019 04:17:11 -0700