给定一个字符串s,找出s中的最长回文串,假定s的最大长度是1000。
1. 暴力法(超时)
对于字符串s的每一个子串s[i,j),判断它是否是回文串,如果是,记下该字符串和它的长度,当遍历完所有的子串时,就可以找到满足要求的子串。
public String longestPalindrome(String s)
{
String ans = null;
int i = 0, j = 0;
int maxlen = 0;
String subStr = null;
// 对于s的每一个子串,检查它是否是回文串 [i,j)
for (j=0; j<s.length(); j++)
{
for (i=0; i<s.length(); i++)
{
if (i+j+1 >= s.length())
{
subStr = s.substring(i, s.length());
}
else
{
subStr = s.substring(i, i+j+1);
System.out.println(subStr);
}
if (isPalindromicStr(subStr))
{
if (subStr.length() > maxlen)
{
maxlen = subStr.length();
ans = subStr;
}
}
}
}
return ans;
}
public static boolean isPalindromicStr(String subStr)
{
if (subStr.length() <= 1)
{
return true;
}
for (int i=0; i<subStr.length()/2; i++)
{
if (subStr.charAt(i) != subStr.charAt(subStr.length()-1-i))
{
return false;
}
}
return true;
}
时间复杂度:O(n^3) , n位字符串s的长度
空间复杂度:O(1)
2. 最长公共子串
一般来说,我们认为翻转字符串S得到S', 找出字符串S和S'的最长公共子串,这同样也是最长回文子串。
例如,S = "caba", S' = “abac”,最长公共子串是“aba",同样也是最长回文串。
但是,当在字符串S的中存在一个非回文的子串的反向拷贝时,该方法会失败。
比如:S="abacdfgdcaba", S' = "abacdgfdcaba",它们之间的最长公共子串是"abacd",但这并不是最长回文串。
为了纠正这一点,每当我们发现一个最长公共子串时的候选集时,我们需要检查该子串的索引是否与逆序后该子串的索引相同,
如果满足该条件,我们尝试更新到目前为止所发现的最长的回文串,否则的话,就跳过该串,继续寻找下一个候选者。
public String longestPalindrome(String s)
{
StringBuffer sBuffer = new StringBuffer(s);
String reverse = sBuffer.reverse().toString();
// 找出字符串s和字符串sBuffer的最长公共子串
String common = getLongestSubString(s, reverse);
return common;
}
/**
* 寻找字符串s1和字符串s2的最长公共子串
*/
public String getLongestSubString(String s1, String s2)
{
String common = ""; // 最长公共回文串
int maxlen = 0;
int len = 0; // 每一段公共子串的长度
int i = 0, j = 0, m=0, n = 0;
// 如果s1或s2是空串
if (s1.isEmpty() || s2.isEmpty())
return common;
char[] array1 = s1.toCharArray();
char[] array2 = s2.toCharArray();
for (i=0; i<array1.length; i++)
{
for (j=0; j<array2.length; j++)
{
if (array1[i] == array2[j])
{
m = i;
n = j;
while ( m < array1.length && n < array2.length && array1[m] == array2[n])
{
len++;
// 这里需要对每一个公共子串进行判断
if (len > maxlen)
{
// 判断该公共子串是否是回文串
if (isPalindStr(s1.substring(i, m+1)))
{
common = s1.substring(i, m+1);
maxlen = len;
}
}
// 更新两个数组比较的下标
m++;
n++;
}
len = 0; // 清零
}
}
}
return common;
}
/**
* 判断字符串s是否是回文串
*/
public boolean isPalindStr(String common)
{
// 检查该公共子串是否是回文串
for (int i=0; i<common.length()/2; i++)
{
if (common.charAt(i) != common.charAt(common.length()-i-1))
{
return false;
}
}
return true;
}
采用动态规划来求解最长公共子串效率要高很多。
3. 扩展中心
一个回文串以它的中心对称,因此一个回文串可以从它的中心开始拓展,这样的中心共有2n-1个。
因为对称线除了是某个字母外,还有可能在两个字母之间。比如回文串“abba”。
public String longestPalindrome(String s)
{
int start = 0, end = 0;
for (int i=0; i<s.length(); i++)
{
// 以i作为对称中心进行拓展
int len1 = expandAroundCenter(s, i, i);
// 以i和i+1的中间作为对称中心进行拓展
int len2 = expandAroundCenter(s, i, i+1);
int len = Math.max(len1, len2);
// 如果新找到的回文串长度比之前的要大
if (len > end - start)
{
start = i - (len - 1) / 2;
end = i + len / 2;
}
}
return s.substring(start, end+1);
}
private int expandAroundCenter(String s, int left, int right)
{
int L = left, R = right;
while (L>=0 && R < s.length() && s.charAt(L) == s.charAt(R))
{
L--;
R++;
}
return R - L - 1; // 回文串的长度
}
时间复杂度:O(n^2)
空间复杂度:O(1)