您的位置:首页 > 职场人生

[LeetCode] Longest Palindrome Substring 详细分析

2014-10-01 11:39 477 查看
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.

面DP题的考官都是神经病。。(吐槽)

貌似挺容易出回文的题,那么今天仔细分析下DP的做法!!以解我被DP问题虐成渣渣的心碎感觉。如有错误请指出~

首先,拿了这个题,至少也可以给出一个O(n^3)的算法,2层for循环加每次检查一遍substring是不是回文。我给出了一个必定超时的代码以供参考。

public String longestPalindrome(String s) {
if(s==null) return null;
// String[][] dp= new String[1000][1000];
// Arrays.fill(dp,1);
int max=1;
String result=s.substring(0,1);
for(int i=0;i<s.length()-1;i++){
for(int j=i+1;j<s.length();j++){
String temp=s.substring(i,j+1);
if(ifPalin(temp)){
if(max<temp.length()){
max=temp.length();
result=temp;
}
if(max==s.length())
return result;
}
}
}

return result;

}

public boolean ifPalin(String s){
if(s==null) return true;
int start=0;
int end=s.length()-1;

while(start<end){
if(s.charAt(start++)!=s.charAt(end--)){
return false;
}
}

return true;
}


这显然不够!如何优化?你会想到这种恶心题,dp那是必然的。那么要分析回文,必定要考虑到所有的情况。

↑上面Naive的方法超时原因是因为:每次循环,都得看一遍当前子串是不是回文,那么如何解决这种重复性的工作呢?

通过对回文性质的分析,我们可以看出来: 如果中间的某一段子串sub是回文,如果sub两侧的字符是一样的,那么作为一个整体也是回文。例如:

abcdcba : d是回文,cdc那么就是回文。 如果你再想一下,cdc是回文,两侧b是一样的,bcdcb也是回文。咦,那么abcdcba就是回文了呗。

通过上个例子我们直接知道了答案。这是特例,那么对于一般情况如何是好呢?所以第一个思路就是根据顺向的思维推导出来的:

我们申明一个dp[][]来储存状态。 对于dp[i][j]来说, 其意义就是 s.substring(i,j+1)是不是回文。

(1)既然我们不知道哪里为中心,向外拓展就能得到答案。那么就以每个字符为中心,向外拓展,保存一个最大的量,问题就解决了!

我们得初始化下dp, 单个字符是,所以dp[i][i]=true.

可是还有种情况, abcddcba,中间是两个,不是一个,这也能构成回文,这怎么办!

其实也好办,我们就看看s.charAt(i)==s.charAt(i+1),如果是,就是回文,我们就set dp[i][i+1]=true

有了基础的case,我们1层for循环移动中心点,用两个指针start,end,分别拓展这两种情况,来分析回文,即可找出最大值。代码如下:

public String longestPalindrome(String s) {
if(s==null) return null;
boolean[][] dp= new boolean[s.length()][s.length()];
//define dp[i][j] is if s.substring(i,j+1) is palindrome:
int max=1;
String result=s.substring(0,1);
for(int i=0;i<s.length();i++){
dp[i][i]=true;
}
for(int i=0;i<s.length()-1;i++){
if(s.charAt(i)==s.charAt(i+1)){
dp[i][i+1]=true;
result=s.substring(i,i+2);
max=2;
}
}
for(int i=1;i<s.length()-1;i++){
int start=i-1,end=i+1;  //aba的情况
while(start>=0 && end<=s.length()-1){
if(dp[i][i] && s.charAt(start)==s.charAt(end)){
dp[start][end]=true;
if(max<end-start+1){
max=end-start+1;
result=s.substring(start,end+1);
}
} else {
break;
}
start--;end++;
}
start=i-1;end=i+2;  //abba的情况
while(start>=0 && end<=s.length()-1){
if(dp[i][i+1] && s.charAt(start)==s.charAt(end)){
dp[start][end]=true;
if(max<end-start+1){
max=end-start+1;
result=s.substring(start,end+1);
}
} else{
break;
}
start--;end++;
}
}

return result;

}


上面的代码啰嗦了点,应该能简化,我比较懒。。如果理解意思了就好办!

(2)上面的解法其实不太算dp,我们来考虑下真正的dp解法,但这个有时候很难想出来。。多琢磨吧。。

至少,如果你看到了这,耐心点再看一些,一定会理解dp解这个题的过程:

dp的思路是用以前做出来的结果作为基础,然后推导出新的结果。(2)的思路是:利用回文的性质,如果判断dp[i][j]是不是回文,那么要具备什么?

答案是:

1. s.charAt(i)==s.charAt(j)

2. dp[i+1][j-1] 是否为真

(这个代表当前的s.substring(i,j+1)往里面缩一个的substring是否是回文,有点绕。。举例: abccbfee, 如果i=0,j=5,也就是abccbf,dp[i][j]就是说abccbf是不是回文,根据之前的结果,如果s.charAt(i)==s.charAt(j),并且,bccb也是回文,那么dp[i][j]就为真. 这个例子中 a!=f, 虽然dp[i+1][j-1]
(bccb)是回文,那也不能算真)

所以这样dp的精华就体现出来了:通过之前的结果,推导现在!

还有个关键点,就是dp的写法。下午我把dp数组打印了出来,横坐标i从后往前,j是从i 到最后。1代表true,是回文,0反之。

dp里面挺多是从后面来的,当然我们不能记结果,而是要明白为什么这么做。





回文的中心多数应该是在中心位置,所以,从后往前走,我们可以利用到之前的结果。 (从前往后走也是一样的,只不过循环的参数要相应的变一下)

然而是上面的条件还不够!如果说dp[i+1][j-1]为假,

但当i和j位置的字符是一致的,并且距离小于等于2的时候,那都是回文,原因是只有3种情况: a (i=j) aa (i=0,j=1) a*a(i=0,j=2) *表示任意字符. 所以这样我们可以当做回文。

接着i不动,j接着走,超出了2的距离,也有可能会遇到和i一样的字符,这时候是不是回文?那就得看dp[i+1][j-1]是不是回文,如果是,记录,判断是否超过当前最大长度。如果超过就替换最长字符串。

看第二张图,那三个1,其实就是例子中 abccbadd的推导过程, 当i=2的循环,j=2,是回文,记录。 i=2, j=3,“cc” 是回文,继续。 "ccb"不是回文,

以上就是全部的分析,建议看文章的童鞋自己推导一遍过程。只有理解了为什么这么做,才能真正理解dp,从而写出出精炼干净的dp代码

:下面代码是从大神那看到的,拿过来的。

public String longestPalindrome(String s) {
if(s==null || s.length()==1) return s;
boolean[][] dp= new boolean[s.length()][s.length()];
//define dp[i][j] is if s.substring(i,j+1) is palindrome:
int max=0;
String result=s.substring(0,1);
for(int i=s.length()-1;i>=0;i--){
for(int j=i;j<s.length();j++){
if(s.charAt(i)==s.charAt(j) && ((j-i<=2) || dp[i+1][j-1])){
dp[i][j]=true;
if(max<j-i+1){
max=j-i+1;
result=s.substring(i,j+1);
}
}
}
}
return result;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息