[leetcode]5.Longest Palindromic Substring
自己的暴力解法果真超時了,但是很開心有進步,能把自己腦子裡的演算法給順利翻譯出來了,比剛開始做第一題的感覺好多了,開心。
暴力解法:遍歷
class Solution { public String longestPalindrome(String s) { char []a=s.toCharArray(); int maxCount=0; String sub=""; int j=0; for(int k=0;k<a.length;k++){ for(int i=k;i<a.length;i++){ for( j=i;j<a.length;j++){ int p=i;int q=j; int flag=0; while(p<=q){ if(a[p]!=a[q]){ flag=1; break; } p++; q--; } if(flag==0){ if((j-i+1)>=maxCount){ maxCount=j-i+1; sub=s.substring(i,j+1); } } } } } return sub; } }
solution1
看了一眼solution1的大概,想出了一個動態規劃解法,複雜度為o(n2)
For example, S = “caba”, S’= “abac”. The longest common substring between S and S’ is “aba”, which is the answer.
//運用動態規劃,將s和s'的遍歷結果存為矩陣。 c[i][j]表示原字串s,從s[i]起,向前長度為c[i][j]的substring是迴文數 . 這裡a是原字串,k是翻轉的。 public int [][] matrix(char []a,char[]k){ int m=a.length; int n=k.length; int[][]c=new int[m+1][n+1]; int i=0,j=0; for(i=0;i<=m;i++) c[i][0]=0; for(i=0;i<=n;i++)c[0][i]=0; for(i=1;i<=m;i++) for(j=1;j<=n;j++){ if(a[i-1]==k[j-1]){ c[i][j]=c[i-1][j-1]+1; } else{ c[i][j]=0; } } return c; }
比如s="bfaa” s‘=“aafb” 矩陣為: 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 1 1 0 0 0 1 2 0 0
為了處理方便, 其中第0列和第0行都預先設為0,c[1…s.length][1…s’.length]才是真正的動態規劃矩陣 相當於c[4][2]中的4對應s[3],2對應s’[1]
如果s[i-1]=s’[j-1],則c[i][j]=c[i-1][j-1]+1;否則,c[i][j]=0 (這個規律畫幾個矩陣試試就明白了)
其中最大數可能為最長的迴文數.即 c[4][2]=2,表示從s的第4個字元(s[3])起,向前滑2個,“aa”(s[2],s[3])為最長迴文數。
特殊情況1:
Let’s try another example: S = “aabfgbaa”, S′ = “aabgfbaa”. The longest common substring between S and S′ is “aab”. Clearly, this is not a valid palindrome. 矩陣為: 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 1 2 0 0 0 0 1 2 0 0 0 3 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 0 1 1 0 0 0 0 2 1 0 1 2 0 0 0 0 1 3
此時最大的c[i][j]是c【9】【9】=3,表示baa是我們求的是最大回文數,但這恰巧是要討論的特殊情況,baa它不是真的,所以到這裡就要判斷一遍是否是迴文數.
int p=0;
int q=sub.length-1;
int flag=0;
while(p<=q){
if(sub[p]!=sub[q]){
flag=1;
break;
}
p++;
q--;
}
發現不是。繼續處理。
這種情況下,最長迴文數的範圍就被縮小了,可能在baa中。 如何分析呢? 注意上述矩陣,最大c[i][j]所在的對角線\上的值表示子串baa的可能最大回文數。現在這個問題和原先的問題是一個結構和性質,所以可以採用遞迴的思路
if(flag==1)
{
return longestPalindrome(String.valueOf(sub));
}
else
return String.valueOf(sub);
特殊情況2:
發現又錯了,因為還有"abcdbbfcba"這種情況沒有考慮到。我們上述做法是解決了特殊情況1:真正的結果在子串的子串裡。假如結果在中間那一部分呢?比如這個的“dbbf”中的“bb”才是正確答案。
其實中間這一部分的處理辦法和前面都是一樣的,還是遞迴。 我們主要要判斷中間部分sub2和由上述的sub,哪個的最長迴文數更長?
於是最後一塊的處理變成了,誰長返回誰,注意要保證sub2不為空才能進行處理,所以要判斷 if(s.length()-max-max>0),否則會陣列越界。
if(flag==1)
{
if(s.length()-max-max>0)
return longestPalindrome(String.valueOf(sub)).length()>=longestPalindrome(String.valueOf(sub2)).length()?
longestPalindrome(String.valueOf(sub)):longestPalindrome(String.valueOf(sub2));
else
return longestPalindrome(String.valueOf(sub));
}
else
return String.valueOf(sub);
}
總結: 特殊情況1,包含了最簡單的情況+“aabfgbaa”(aa藏在假的迴文數baa裡) 特殊情況2,補充了"abcdbbfcba"這種最長迴文數在中間的情況。
class Solution {
//運用動態規劃,將s和s'的遍歷結果存為矩陣,c[i][j]表示原字串s,從s[i]起,向前長度為c[i][j]的substring是迴文數
public int [][] matrix(char []a,char[]k){
int m=a.length;
int n=k.length;
int[][]c=new int[m+1][n+1];
int i=0,j=0;
for(i=0;i<=m;i++) c[i][0]=0;
for(i=0;i<=n;i++)c[0][i]=0;
for(i=1;i<=m;i++)
for(j=1;j<=n;j++){
if(a[i-1]==k[j-1]){
c[i][j]=c[i-1][j-1]+1;
}
else{
c[i][j]=0;
}
}
return c;
}
public String longestPalindrome(String s) {
char []a=s.toCharArray();
for (int i=0;i<a.length/2;i++){
char temp=a[i];
a[i]=a[a.length-1-i];
a[a.length-1-i]=temp;
}
char []k=s.toCharArray();
int maxCount=0;
int [][]c=matrix(k,a);
int max=0;
int maxi=0,maxj=0;
for(int i=1;i<=k.length;i++){
for(int j=1;j<=a.length;j++){
if(c[i][j]>=max){
max=c[i][j];
maxi=i;
maxj=j;
}
}
}
char []sub=s.substring(maxi-max,maxi).toCharArray();
char [] sub2=new char[s.length()];
if(s.length()-max-max>0)
sub2=s.substring(max,s.length()-max).toCharArray();
int p=0;
int q=sub.length-1;
int flag=0;
while(p<=q){
if(sub[p]!=sub[q]){
flag=1;
break;
}
p++;
q--;
}
if(flag==1)
{
if(s.length()-max-max>0)
return longestPalindrome(String.valueOf(sub)).length()>=longestPalindrome(String.valueOf(sub2)).length()?
longestPalindrome(String.valueOf(sub)):longestPalindrome(String.valueOf(sub2));
else
return longestPalindrome(String.valueOf(sub));
}
else
return String.valueOf(sub);
}
}