Leetcode – Longest Palindromic Substring (Java)

Finding the longest palindromic substring is a classic problem of coding interview. This post summarizes 3 different solutions for this problem.

1. Dynamic Programming

Let s be the input string, i and j are two indices of the string. Define a 2-dimension array “table” and let table[i][j] denote whether a substring from i to j is palindrome.

Changing condition:

table[i+1][j-1] == 1 && s.charAt(i) == s.charAt(j)
=>
table[i][j] == 1

Time O(n^2) Space O(n^2)

public String longestPalindrome(String s) {
    if(s==null || s.length()<=1)
        return s;
 
    int len = s.length();
    int maxLen = 1;
    boolean [][] dp = new boolean[len][len];
 
    String longest = null;
    for(int l=0; l<s.length(); l++){
        for(int i=0; i<len-l; i++){
            int j = i+l;
            if(s.charAt(i)==s.charAt(j) && (j-i<=2||dp[i+1][j-1])){
                dp[i][j]=true;
 
                if(j-i+1>maxLen){
                   maxLen = j-i+1; 
                   longest = s.substring(i, j+1);
                }
            }
        }
    }
 
    return longest;
}

For example, if the input string is “dabcba”, the final matrix would be the following:

1 0 0 0 0 0 
0 1 0 0 0 1 
0 0 1 0 1 0 
0 0 0 1 0 0 
0 0 0 0 1 0 
0 0 0 0 0 1 

From the table, we can clearly see that the longest string is in cell table[1][5].

2. A Simple Algorithm

We can scan to both sides for each character. Time O(n^2), Space O(1)

public String longestPalindrome(String s) {
	if (s.isEmpty()) {
		return null;
	}
 
	if (s.length() == 1) {
		return s;
	}
 
	String longest = s.substring(0, 1);
	for (int i = 0; i < s.length(); i++) {
		// get longest palindrome with center of i
		String tmp = helper(s, i, i);
		if (tmp.length() > longest.length()) {
			longest = tmp;
		}
 
		// get longest palindrome with center of i, i+1
		tmp = helper(s, i, i + 1);
		if (tmp.length() > longest.length()) {
			longest = tmp;
		}
	}
 
	return longest;
}
 
// Given a center, either one letter or two letter, 
// Find longest palindrome
public String helper(String s, int begin, int end) {
	while (begin >= 0 && end <= s.length() - 1 && s.charAt(begin) == s.charAt(end)) {
		begin--;
		end++;
	}
	return s.substring(begin + 1, end);
}

3. Manacher’s Algorithm

Manacher’s algorithm is much more complicated to figure out, even though it will bring benefit of time complexity of O(n). Since it is not typical, there is no need to waste time on that.

46 thoughts on “Leetcode – Longest Palindromic Substring (Java)”

  1. /*
    Runtime: 192 ms, faster than 33.89% of Java online submissions for Longest Palindromic Substring.
    Memory Usage: 44.8 MB, less than 16.66% of Java online submissions for Longest Palindromic Substring.
    */

    //Dynamic Programming working solution


    private String longestPalindrome(String s){
    if(s==null || s.length()<=1)
    return s;

    if(s.length() == 2 && s.charAt(0) != s.charAt(1)) return String.valueOf(s.charAt(0));

    int len = s.length();
    int maxLen = 1;
    boolean [][] dp = new boolean[len][len];

    String longest = null;
    for(int l=0; l<s.length(); l++){
    for(int i=0; i<len-l; i++){
    int j = i+l;
    if(s.charAt(i)==s.charAt(j) && (j-imaxLen){
    maxLen = j-i+1;
    longest = s.substring(i, j+1);
    }
    }
    }
    }

    if(longest == null) return String.valueOf(s.charAt(0));

    return longest;

    }

  2. y keep two helper method call in second solution. both helper call does same right? how it works second helper return value replace the first helper returned value right then y keep this extra second method call?

  3. Both sides of the entire string? So each character is like a mid-point, and then all the characters to the left are tested if they’re a palindrome, vice versa with right side?

  4. Hi, can you explain the second “simple” algorithm in more detail? I understand the first algorithm because its plain to see how it works with the 2D array. But I can’t really visualize the second one.

  5. Here is my implementation. Simple and efficient

    public String LongestPalindrome(String s) {
    if(s == null || s.length() == 0) return null;
    if(s.length() == 1) return s;

    StringBuilder longestPalin = new StringBuilder();
    int max = 0;

    for (int i = 0; i < s.length(); i++) {
    for (int j = i + 2; j max) {
    longestPalin.setLength(0);
    longestPalin.append(sb.toString());
    max = sb.toString().length();
    }
    }
    }
    return longestPalin.toString();
    }

  6. Hi Doesnt the code given under dynamic programming section throw a index out of bounds exception when l=s.length()-2 and i=s.length()-1. then j=i+l which is 2.s.length()-3. So s.charAt(j) gives index out of bounds exception

  7. in scala

    “`
    def findPS(os: String) = {
    def isPS(s: String): Boolean = s == s.reverse
    val idxedS = os.toList.zip(0 until os.length)
    val idxDict = idxedS
    .groupBy[Char]{ case (c, idx) => c }
    .map {case (c, tps) => (c, tps.map(_._2).sorted.reverse)}
    var (psLength, psStr) = (1, “”)
    idxedS.foreach { case (c, idx) =>
    idxDict(c).takeWhile(_ > idx).foreach { nIdx =>
    os.substring(idx, nIdx + 1) match {
    case subS if isPS(subS) && nIdx-idx > psLength =>
    psLength = nIdx – idx + 1
    psStr = subS
    case _ => ()
    }
    }
    }
    psStr
    }
    “`

  8. public class LongestPalindrome
    {
    public static void main(String[] args)
    {
    String input=”abccbp”;
    String longest=longestPalindrome(input);
    System.out.println(longest);
    }

    public static String longestPalindrome(String input)
    {
    if(input.isEmpty())
    return input;

    if(input.length()==1)
    return input;

    String longest=””;
    String output=””;
    int length=0;

    for(int i=0;i<input.length();i++)
    {
    longest="";

    for(int j=i;jlength)
    {
    length=longest.length();
    output=longest;
    }
    }
    }
    }

    return output;
    }
    }

  9. I think Solution 3 expects the characters to be adjacent. This question usually is just about finding longest NOT necessarily contiguous substring. The recursive approach of this problem will be exponential time, 2^n . But using dynamic programming time can be reduced to n^2

  10. ABCCBP iis working fine. But incase I give ABCDA, the longest possible palindrome is ‘AA’ but it is showing ‘A’. Hope I didn’t unnderstand it wrong..!

  11. I did this in pseudo code since I am novice at Java
    test: “satdoggodabca”
    –main:
    —-loop (idx=0;idx<length;idx++):
    ——get char (baseChar) at idx
    ——loop:
    ——–find char again in test (testChar)
    ——–test if testChar– matches baseChar++, loop until indexes are equal, store if equal

    visual representation:

    1. satdoggodabca

    2. s_____________ (no other 's' found) t: length

    3. _a________a__a (test if inner characters of found 'a' match the next character of the other 'a') t:length – 1

    3. _at______da_ca ( no matches here, continue) t: # of found chars

    4. __t___________ ( none for 't') t: length – 3

    5. ___d_____d___ ( test 'd') t: length – 4

    5. ___do__od___ ( looking good) t: # of found chars

    5. ___doggod___ (yeeee, also note since it is left most and we started by whole string scanning, we can assume this is the longest palindrome)

    estimated worst time: O(n * (n-k))

  12. The DP solution above did not pass the test case which is the “aaa…aabcaa…aaa”, its length is 1,000 and the ‘b’ is in index 498. OJ shows that “Time Limit Exceeded”.

  13. What do you guys think of this solution. This is an O(n) solution

    private String findLongestPalindromicSubString(String str){

    if(str == null || str.length() == 0)
    return null;
    else if(str.length() == 1)
    return str;
    else{
    //Traverse down the string and which is each character
    //check this logic.
    int index=1;
    String longestPalindrome=null;
    while(index < str.length()/*||
    (longestPalindrome != null && str.length() – index 2 && str.charAt(index) == str.charAt(index – 2))
    currentPalindrome=readPalindrome(index – 2, index, str);

    if(currentPalindrome != null){
    if(longestPalindrome == null || currentPalindrome.length() > longestPalindrome.length())
    longestPalindrome=currentPalindrome;
    index+=currentPalindrome.length() / 2;
    }else
    index++;
    }
    return longestPalindrome;
    }
    }

  14. The bounds checking of the helper function is wrong. Why does the helper func return the substring from begin + 1? and what if the while loop breaks at end = s.length?

    If the loop breaks before begin < 0 and before end = s.length it should just return substr(begin, end).

    so somethin like:

    if (begin s.length – 1) end = s.length -1;

    return s.substring(begin, end);

  15. Hello,

    Can someone let me know what is the time and space complexity for my solution to this problem below?


    public static String longestPalindrome(String[] stringArray) {
    if (stringArray.length == 1) {
    return stringArray[0];
    }
    if (stringArray.length == 0) {
    return null;
    }
    int maxLength = 0;
    String longestPal = "";
    for (final String eachString : stringArray) {
    if (isPalindrome(eachString) && eachString.length() > maxLength) {
    maxLength = eachString.length();
    longestPal = eachString;
    }
    }
    return longestPal;
    }

    public static boolean isPalindrome(String testStr) {
    int i = 0, j = testStr.length() - 1;
    while (i != j) {
    if (testStr.charAt(i) == testStr.charAt(j)) {
    i++;
    j--;
    } else {
    return false;
    }
    }
    return true;
    }

  16. Hello,

    Can someone let me know what would be the time and space complexity of my solution here?

    public static String longestPalindrome(String[] stringArray) {

    if (stringArray.length == 1) {

    return stringArray[0];

    }

    if (stringArray.length == 0) {

    return null;

    }

    int maxLength = 0;

    String longestPal = “”;

    for (final String eachString : stringArray) {

    if (isPalindrome(eachString) && eachString.length() > maxLength) {

    maxLength = eachString.length();

    longestPal = eachString;

    }

    }

    return longestPal;

    }

    public static boolean isPalindrome(String testStr) {

    int i = 0, j = testStr.length() – 1;

    while (i != j) {

    if (testStr.charAt(i) == testStr.charAt(j)) {

    i++;

    j–;

    } else {

    return false;

    }

    }

    return true;

    }

  17. I think Kishore is talking about solution 1, public static boolean isPalindrome(String s);. Since this method is checking whether the passed string instance is a palindrome or not, you only need to iterate half of its length; otherwise, we are checking the same value twice if the string is palindrome.

  18. to reduce the number of iterations in the first case

    public static boolean isPalindrome(String s) {
    int j = (s.length()%2)!=0?(s.length()/2):((s.length()/2)+1);
    int i=0;
    for (i = 0; i < j; i++) {
    if (s.charAt(i) != s.charAt((s.length() – 1) – i)) {
    return false;
    }
    }
    return true;
    }

  19. Can anyone help me finding the complexity of this? ->
    private static boolean checkPaliSubstring(String str1) {
    boolean paliSubstr = false;
    int longestlen = 0;

    String sub1 = str1.substring(1);
    if(checkPalindrome(sub1) && sub1.length()>longestlen){
    longestSubPalindrome= sub1;//class field
    longestlen = sub1.length();
    paliSubstr = true;
    }

    String sub2 = str1.substring(0, str1.length()-1);
    if(checkPalindrome(sub2) && sub2.length()>longestlen){
    longestSubPalindrome= sub2;//class field
    longestlen = sub2.length();
    paliSubstr = true;
    }

    if(!paliSubstr && sub1.length()!=2){
    checkPaliSubstring(sub1);
    }

    if(!paliSubstr && sub2.length()!=2){
    checkPaliSubstring(sub2);
    }

    return paliSubstr;//substring palindrome exists or not
    }

  20. Can we put indices of all occurrences of chars in a hash table and then pursue just ones with count >2 ? Could be simpler IMO

  21. table[i][i] and table[i][i+1] is calculated in the first two for loop.
    l=3 to start the third for loop is to solve the condition that table[i][j] where the gap between i and j is above 2.

  22. I feel, can be slightly optimized of we jump to end of the present max. something like this. Observe for j. (Let me know whether it is correct or can miss some scenarios).

    int j=0;
    String longest = s.substring(0, 1);
    for (int i = 0; i longest.length()) {
    longest = tmp;
    j = tmp.length()/2;
    }

    // get longest palindrome with center of i, i+1
    tmp = helper(s, i, i + 1);
    if (tmp.length() > longest.length()) {
    longest = tmp;
    j = tmp.length()/2;
    }

  23. There could be the palindrome in the second half. It always need not be the spread around the center.

  24. My solution: Time O(n^2) Space O(1)

    public String longestPalindrome(String s) {
    if (s.isEmpty()) {
    return null;
    }

    if (s.length() == 1) {
    return s;
    }

    String longest = s.substring(0, 1);
    for (int i = 0; i longest.length()) {
    longest = tmp;
    }

    // test s[i,i+1]
    tmp = helper(s, i, i + 1);
    if (tmp.length() > longest.length()) {
    longest = tmp;
    }
    }
    return longest;
    }

    // [begin, end]
    public String helper(String s, int begin, int end) {
    while (begin >= 0
    && end <= s.length() – 1
    && s.charAt(begin) == s.charAt(end)) {
    begin–;
    end++;
    }
    return s.substring(begin + 1, end);
    }

Leave a Comment