Reputation:
This code doesn't work for the test case: aacabdkacaa
, but it works for babad
or cbbd
. I wrote a print statement to debug and realized the code thinks that the strings aacakdbacaa
and aacabdkacaa
are equivalent for some reason. They clearly aren't, so what am I missing?
class Solution {
public String longestPalindrome(String s) {
String longPal = "";
for(int i = 0; i < s.length(); i++) {
for(int j = s.length()-1; j >= i; j--) {
if(s.charAt(i) == s.charAt(j)) {
StringBuilder sb = new StringBuilder(s.substring(i, j+1));
if(sb.reverse().toString().equals(sb.toString())
&& sb.toString().length() > longPal.length()) {
longPal = sb.toString();
System.out.println(sb.toString()+" equals "+sb.reverse().toString());
}
sb.setLength(0);
}
}
}
return longPal;
}
}
Upvotes: 1
Views: 131
Reputation: 6589
The code does not think that aacakdbacaa
and aacabdkacaa
are equal.
The first part of the condition in the if
statement is this
sb.reverse().toString().equals(sb.toString())
So what the code (JVM) thinks is that this condition is true
.
Why is that? What hidden knowledge is behind this (because there is some)?
The hidden knowledge part (it is not really hidden, it is in the documentation) is that StringBuilder reverse
method changes the state of the StringBuilder (almost all SB methods do). The reverse
method also returns the StringBuilder instance itself (some other SB methods do as well).
So the call to sb.reverse()
just reverses the contents of the StringBuilder instance and returns the instance itself (the same instance that is!). The instance hasn't changed, just its inner state has changed.
Then we call toString()
on it, which returns the String
representation of the current state (as expected).
Then we call equals
on the returned String
and we pass into equals
sb.toString()
as the value for the first parameter. But sb
is the same instance as what was returned from sb.reverse()
before. Its state hasn't changed in between, so we get the same String
representation returned from the call to sb.toString()
.
That is why
sb.reverse().toString().equals(sb.toString())
is true.
We could write it a bit differently and still get the same result
String s1 = sb.reverse().toString() // sb state changed here when .reverse() was called
String s2 = sb.toString()
s1.equals(s2) // true
Or like this, which makes it quite obvious
sb.reverse() // sb state changed here
String s1 = sb.toString()
String s2 = sb.toString()
s1.equals(s2) // true
However, we get a different result, if we use this order
String s1 = sb.toString()
sb.reverse() // sb state changed here
String s2 = sb.toString()
s1.equals(s2) // now it depends, true if palindrome, false otherwise!
which is the same as
String s1 = sb.toString()
String s2 = sb.reverse().toString()
s1.equals(s2) // true if palindrome, false otherwise!
So this would be the correct check to check for a palindrome
sb.toString().equals(sb.reverse().toString())
Bonus: The practice to return the same instance from a method call is called Fluent interface.
Upvotes: 2
Reputation: 44
StringBuilder's reverse() method causes it's character sequence to be replaced by the reverse of the sequence, so sb.reverse().toString().equals(sb.toString()) always results true.
declare another string String p = sb.toString()
then use it to compare sb.reverse().toString().equals(p.toString())
may solve the problem.
Upvotes: 1