题目:Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.
个人理解:求给定字符串的最长回文。
我首先想到的是从左到右找出该字符串里的所有回文,然后每次都比较找到的回文,保存最长的那条。这种方法是很笨的,因为它把所有相邻的字符串组合都查了个遍,结果虽然正确,但是超时了。
public class Solution {
public String longestPalindrome(String s) {
char[] inputArray = s.toCharArray();
int length = inputArray.length;
if (length == 0) {
return "";
}
if (length == 1) {
return s;
}
String longestPalidrome = "";
for (int i = 0; i < length; i++) {
for (int j = i + 1; j < length; j++) {
// 从i位向后寻找
for (int k = i, m = j;; k++, m--) {
if (inputArray[k] == inputArray[m]) {
// 有可能是回文
if (k >= m) {
// 这是一个回文,和之前的回文对比,看看哪条长,保存最长的,然后返回上层循环继续寻找
String tempPalidrome = new String(s.substring(i, j + 1));
longestPalidrome = longestPalidrome.length() > tempPalidrome.length() ? longestPalidrome : tempPalidrome;
break;
} else {
// 有可能是回文,继续当前循环的寻找工作
continue;
}
} else {
// 不是回文,返回上一层循环继续寻找
break;
}
}
}
}
return longestPalidrome.length() < 2 ? String.valueOf(inputArray[length - 1]) : longestPalidrome;
}
}
后来我就想,不要傻傻地列出所有的字符串组合,再去判断是不是回文,是不是最长回文,我直接从回文的中心去找回文字符串,找到直接判断是不是最长的不久行了吗?找到方向之后,分析一下,发现回文其实就是分两种情况,一种是字符个数为奇数,一种是字符个数是偶数,然后就开始写代码了:
public class Solution {
public String longestPalindrome(String s) {
char[] inputArray = s.toCharArray();
int length = inputArray.length;
int longestIndex = length - 1;
if (length == 0) {
return "";
}
if (length == 1) {
return s;
}
int longestBegin = 0;
int longestEnd = 0;
int longestLength = 1;
// 找出所有单枢纽回文比较之,i就是枢纽
int i = 1;
while (i < longestIndex) {
int j = i - 1, k = i + 1;
while (j >= 0 && k < length) {
if (inputArray[j] != inputArray[k]) {
// 当前不是回文了
break;
}
j--;
k++;
}
// 更新找到回文信息
int tempPalindromeLength = (k - 1) - (j + 1) + 1;
if (longestLength < tempPalindromeLength) {
longestLength = tempPalindromeLength;
longestBegin = j + 1;
longestEnd = k - 1;
}
i++;
}
// 找出所有双枢纽回文比较之
int u = 0;
while (u < longestIndex) {
// x是枢纽1,y是枢纽2
int x = u, y = u + 1;
// 定义一个标志来记住是否是真正的双枢纽
boolean isRealDoublePivot = true;
while (x >= 0 && y < length) {
if (inputArray[x] != inputArray[y]) {
// 当前不是回文了
if (y - x == 1) {
// 不是真正的双枢纽
isRealDoublePivot = false;
}
break;
}
x--;
y++;
}
// 找到的确实是双枢纽,更新回文信息
if (isRealDoublePivot) {
int tempPalindromeLength = (y - 1) - (x + 1) +1;
if (longestLength < tempPalindromeLength) {
longestLength = tempPalindromeLength;
longestBegin = x + 1;
longestEnd = y - 1;
}
}
u++;
}
return longestLength < 2 ? String.valueOf(inputArray[0]) : s.substring(longestBegin, longestEnd + 1);
}
}
后来脑袋闪过一个念头,我们的目的不是求最长的回文吗?回文长度再长,也长不过我们输入的字符串,那么我们假设最长回文的长度就是输入的字符串长度,如果假设被推翻,我们就假设最长回文的长度是输入字符串长度-1。如此递减地找,找到的那条,肯定是最长的了,因为我们的假设是从最长开始的。这样也是可以通过的,不过速度好像没有第二种方案快?
public class Solution {
public String longestPalindrome(String s) {
char[] inputArray = s.toCharArray();
int length = inputArray.length;
int longestIndex = length - 1;
if (length == 0) {
return "";
}
if (length == 1) {
return s;
}
// 回文的长度再怎么长也长不过输入的字符串长度
for (int palindromeLen = length; palindromeLen > 0; palindromeLen--) {
if (palindromeLen % 2 == 0) {
// 回文长度为偶数,枢纽的坐标为palindromeLen/2-1,palindromeLen/2到length-palindromeLen/2-1,length-palindromeLen/2
for (int i = palindromeLen/2-1, j = length-palindromeLen/2-1; i <= j; i++) {
int m = i, n = i + 1;
while (m >= 0 && n <= longestIndex) {
// 中心两个数相等,那么向两边扩散
if (inputArray[m] == inputArray[n]) {
if (n - m == palindromeLen - 1) {
// 找到最长的回文字符串了
return s.substring(m, n + 1);
}
m--;
n++;
} else {
// 还没到达指定的长度就不相等了,跳出当前枢纽的扩散操作,转战下一个枢纽
break;
}
}
}
} else {
// 回文长度为奇数,枢纽的坐标为palindromeLen/2到length-palindromeLen/2-1
for (int i = palindromeLen/2, j = length-palindromeLen/2-1; i <= j; i++) {
int m = i - 1, n = i + 1;
while (m >= 0 && n <= longestIndex) {
// 中心两个数相等,那么向两边扩散
if (inputArray[m] == inputArray[n]) {
if (n - m == palindromeLen - 1) {
// 找到最长的回文字符串了
return s.substring(m, n + 1);
}
m--;
n++;
} else {
// 还没到达指定的长度就不相等了,跳出当前枢纽的扩散操作,转战下一个枢纽
break;
}
}
}
}
}
return String.valueOf(inputArray[0]);
}
}