对于这道中等题,没有什么好的办法,就想到一般做法:将字符串从前到后遍历一遍,每次找到回文串,将它放到HashMap里,最后比较一下大小。对我来说,难度在于如何记录最长回文串和如何降低时间复杂度。我的代码如下,时间579 ms,程序之所以没有超出时间限制,我也是做了修改:
(1)每次遍历字符串,都是在之前HashMap中的最大的长度上继续搜索,节约一定时间;
(2)for循环中使用++i,代替之前的i++,因为i++是需要临时变量去存储返回自增前的值,而++i不需要;
但是效率低,终究是效率低!
class Solution {
public static String longestPalindrome(String s) {
int len = s.length();
HashMap re = new HashMap<>();
re.put(0, "");
for (int i = 0; i < len; ++i) {
for (int j = len - 1; j >= i + Collections.max(re.keySet()); --j) {
if (s.charAt(i) == s.charAt(j)) {
String str = s.substring(i, j + 1);
if (isPalindrome(str)) {
if (str.length() == s.length())
return s;
if (str.length() > Collections.max(re.keySet())) {
re.put(str.length(), str);
break;
}
}
}
}
}
return re.get(Collections.max(re.keySet()));
}
public static boolean isPalindrome(String text) {
int length = text.length();
for (int i = 0; i <= length / 2; ++i) {
if (text.charAt(i) != text.charAt(length - i - 1))
return false;
}
return true;
}
}
后来我又参考别人的思路,其实一开始我看得有点懵逼,调试了一遍稍微理解了一点:
假设输入的字符串为 s,借助两个变量来保存最长回文字串的信息,分别为 l 和 j。其中 l 表示最长回文子串的长度,j 表示最长回文子串的尾字符在 s 中的下标,还使用变量 ll 表示以当前字符为边界的最长回文子串的长度。
如果当前遍历到 s 的第 i (从0开始) 个字符 (此时的 l 和 j 保存的是 s[0…i-1] 的最长回文子串的信息),通过第 i-1 个字符的 ll 来判断当前字符有没有增加前一个最长回文子串的长度,如果增加了,则 ll 自增 2,此时更新后的 ll 代表的就是以第 i 个字符为边界的最长回文子串的长度。如果没有增加,就需要重新确定以第 i 个字符为边界的最长回文子串的长度,确认范围为从 ll+1 到 1。ll 更新后,比较 ll 与 l 的大小,如果 ll 变得比 l 要大,则将当前 ll 的值赋给 l,i 的值赋给 j。
class Solution {
public static boolean isPalindrome(String s, int b, int e) {
int i = b, j = e;
while(i <= j) {
if(s.charAt(i) != s.charAt(j)) return false;
++i;
--j;
}
return true;
}
public static String longestPalindrome(String s) {
if(s.length() <=1){
return s;
}
int l = 1, j = 0, ll = 1;
for(int i = 1; i < s.length(); ++i) {
// 下面这个if语句,ll恒表示:以第i个字符为尾的最长回文子串的长度
if(i - 1 - ll >= 0 && s.charAt(i) == s.charAt(i - 1 - ll)) {
ll += 2;
}
else{
while(true){
// 重新确定以i为边界,最长的回文字串长度。确认范围为从ll+1到1
if(ll == 0 || isPalindrome(s, i - ll, i)) {
++ll;
break;
}
--ll;
}
}
if(ll > l){
// 更新最长回文子串信息
l = ll;
j = i;
}
}
// 返回从j-l+1到j长度为l的子串
return s.substring(j - l + 1, j + 1);
}
}
这道题还有动态规划和Manacher算法求解,链接在此,java实现如下。
动态规划:
class Solution {
public String longestPalindrome(String s) {
if (s.length() < 2)
return s;
int start = 0;
int longest = 1;
boolean[][] arr = new boolean[s.length()][s.length()];
for (int i = 0; i < s.length(); ++i) {
arr[i][i] = true;
if (i < s.length() - 1) {
if (s.charAt(i) == s.charAt(i + 1)) {
arr[i][i + 1] = true;
start = i;
longest = 2;
}
}
}
for (int wind = 3; wind <= s.length(); ++wind) {
for (int i = 0; i + wind - 1 < s.length(); ++i) {
int j = i + wind - 1;
if (s.charAt(j) == s.charAt(i) && arr[i + 1][j - 1]) {
arr[i][j] = true;
start = i;
longest = wind;
}
}
}
return s.substring(start, start + longest);
}
}
Manacher算法,算法理解可参考链接:
class Solution {
public String longestPalindrome(String s) {
if (s.length() < 2) return s;
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append('#');
for (int i = 0; i < s.length(); ++i) {
stringBuffer.append(s.charAt(i));
stringBuffer.append('#');
}
String T = stringBuffer.toString();
int len = T.length();
int[] p = new int[len];
int Max = 0;
int MaxId = 0;
int id = 0;
int longestLen = 0;
for (int i = 1; i < len; ++i) {
p[i] = i < Max ?
Math.min(p[2 * MaxId - i], Max - i) :
1;
while (i + p[i] < len
&& i - p[i] >= 0
&& T.charAt(i + p[i]) == T.charAt(i - p[i])) {
p[i]++;
}
if (i + p[i] > Max) {
MaxId = i;
Max = i + p[i];
}
if (p[i] - 1 > longestLen) {
id = (i - p[i] + 1) / 2;
longestLen= p[i] - 1;
}
}
return s.substring(id, longestLen + id);
}
}