最长回文子串问题,题目描述:给定一个字符串 s
,找到 s
中最长的回文子串。你可以假设 s
的最大长度为 1000。
所谓回文串,就是指一个字符串正序和倒序完全一致,必须"aba","baab"等,本题求的是一个字符串最长的回文子串,比如"aabbaccddcc"的最长回文子串就是"ccddcc"。
首先想到的是暴力解法,遍历每一个字符作为子串的开端,字符串从后往前的每一个字符作为子串的结尾,然后再进行回文串的判断,因为要进行三次循环,所以总的时间复杂度为,结果超时,但扔然通过93个测试样例。
暴力法的思路是从两端向中间找出所有的回文子串,这样必然存在大量的重复对比,比如"abdcfdba",a对比了一次,b对比了两次,d对比了三次,明显不合理。
优化暴力方法的思路就是动态规划,动态规划的核心思想就是大问题化为小问题来解决 ,假如已知某子串s1为回文串,比如"abba",那么s加上前一位和向后一位的新子串s2为回文串的条件就是加的这两个字符相同,比如"cabbac",描述如下(截图)。
因此需要一个二维数组来存储的值 ,依次求出长度为1到N(字符串长度)的子串是否为回文串,其中1和2需要单独判断,3到N利用动态方程求得,时间复杂度为,缺点是需要额外的空间存储二维数组,长字符串会有较大开销,实现代码如下。
class Solution {
public String longestPalindrome(String s) {
int length = s.length();
Boolean p[][] = new Boolean[length][length];
int maxlen = 0;
int start=0,end=0;
for(int len = 1 ; len <= length ; ++len)
for(int i = 0; i< length ; ++i)
{
int j = i+len-1;
if(j>=length)
break;
p[i][j] = (len==1||len==2||p[i+1][j-1])&&s.charAt(i)==s.charAt(j);
if(p[i][j]&&len>maxlen)
{
start = i;
end = j;
maxlen = len;
}
}
return length>1?s.substring(start,end+1):s;
}
}
另一种思路就是从中间向两端查找,也就是遍历每一个字符作为回文子串的中间点,然后向两边对比,这样的时间复杂度为。由于回文子串的中心可以是一个字符,也可以是两个字符,所以要分别处理。这种方法也叫做中心扩展法,实现代码如下。
class Solution {
public String longestPalindrome(String s) {
int length = s.length();
if(length<2)
return s;
int start = 0, end = 0, maxlen = 0;
for(int i = 0;i < length;++i)
{
int lenOdd = expandCenter(s,i, i);
int lenEven = expandCenter(s,i, i+1);
int len = Math.max(lenOdd,lenEven);
if(len>maxlen)
{
maxlen = len;
start = i-(len-1)/2;
end = i+len/2;
}
}
return s.substring(start,end+1);
}
public int expandCenter(String s,int L,int R)
{
int len = (L==R?-1:0);
while(L>=0&&R<s.length())
{
if(s.charAt(L)!=s.charAt(R))
break;
len+=2;
L--;
R++;
}
return len;
}
}