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.
Hi guys
This problem can be solved easily using O(n) approach, which can be applied to other palindromic problems as well. Please check the below video
https://www.youtube.com/watch?v=z3d8SbHuRes&t=846s
https://uploads.disquscdn.com/images/d301cc2f8a504454d1c1043f3ff3ed0fea78d15e8a36c3b679035477d5141120.jpg
/*
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.
*/
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;
}
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?
” aba ” will also work, that is also an option
code
Can’t you do it yourself ? Go back to the text books and read about Asymptotic
yeh , the DP solution doesn’t work for me either. Can any one fix it
the second approach are not optimal, if we have
“babad”, it will return “aba” instead of “bab”
Correct. Like this
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?
For each character, scan to both sides and get the longest palindromic sub-strings.
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.
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();
}
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
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
}
“`
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;
}
}
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
yep ! u are right. the objective is to make the length between i and j is 3.
I suppose u at wrong at some point.
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..!
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))
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”.
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;
}
}
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);
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;
}
public static void main(){
}
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;
}
In addition, the outer loop should be. Not necessary, but makes more sense.
for (int i = 0; i < length-1; i++)
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.
For more programs Refer http://javatutorialhere.blogspot.in/
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;
}
@Pankaj: Did you run the program? I executed with ABCCP as input string the output was BCCB
You may want to run the code again. I used “ABCCBP” to test, and it worked.
This would not work correctly, ex. ABCCBP. It wont be able to find exact longest palindrom which is BCCB
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
}
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
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.
Sorry. Wrong Optimization. Found a scenario where it can fail. Your one is the correct one.
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;
}
There could be the palindrome in the second half. It always need not be the spread around the center.
How can i find the longest str by using the final matrix?
In the isPalindrome method, the checking can only be done till
i < s.length()/2
In the DP solution why is the l in the first for loop set to 3?
Good Algorithm.
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);
}