两种思路:
第一种:将问题转化成求s和s逆串的最长连续公共子序列
最长连续公共子序列是在求最长公共子序列基础上增加了连续子序列的限定。先看下最长公共子序列:
假设A="a0,a1,...,am-1", B="b0,b1,...,bn-1", Z="z0,z1,...,zk-1"是A,B的最长公共子序列。
当am-1=bn-1可以分解成一个子问题求”a0,a1,...,am-2"以及"b0,b1,...,bn-2"的最长公共子序列
当am-1!=bn-1则分解成两个子问题
求”a0,a1,...,am-1"以及"b0,b1,...,bn-2"最长公共子序列
求”a0,a1,...,am-2"以及"b0,b1,...,bn-1"最长公共子序列
然后取两者中最长的子序列
问题可以变成如下递归:
可以通过动态规划构建c[i][j]矩阵解决这个问题。
求连续子序列可以直接构建二维矩阵c[][],c[i][j]=1表示s[i]=s'[j]这样最长的连续斜对角线就是最长连续字串。
public String longestPalindrome(String s) {
length=s.length();
int[][]lcs=new int[length][length];
int max=-1;
int index1=0;
int index2=0;
for(int i=0;i<length;i++){
for(int j=0;j<length;j++){
if(s.charAt(i)!=s.charAt(length-1-j))lcs[i][j]=0;
else if(i==0||j==0){
lcs[i][j]=1;
if(lcs[i][j]>max){
index1=i;
index2=j;
max=lcs[i][j];
}
}
else {
lcs[i][j]=lcs[i-1][j-1]+1;
if(lcs[i][j]>max){
index1=i;
index2=j;
max=lcs[i][j];
}
}
}
}
int startindex=index1;
while(index1>=0&&index2>=0){
if(lcs[index1][index2]==0)break;
index1--;
index2--;
}
return s.substring(index1+1,startindex+1);
}
第二种思路:
中心扩展,从中间位置向外扩展,求该位置扩展出的最长回文串。然后在上一层循环中遍历中心点的位置。
private int length=0;
public String longestPalindrome(String s){
if(s==null)return null;
length=s.length();
if(length==1)return s;
String palindromic=s.substring(0,1);
for(int i=0;i<length-1;i++){
String tmp=findLongest(s,i,i);
if(tmp.length()>palindromic.length())palindromic = tmp;
tmp=findLongest(s,i,i+1);
if(tmp.length()>palindromic.length())palindromic=tmp;
}
return palindromic;
}
private String findLongest(String s,int start,int end){
while(start>=0&&end<length&&s.charAt(start)==s.charAt(end)){
start--;
end++;
}
return s.substring(start+1,end);
}
最后发现第二种时间复杂度要小于第一种O(n2),因为第二种在第二层循环中并不需要扩展到n,很多时候会在中间停止。