回文字符串問題


回文字符串問題

一、動態規划法

定義boolean型的 p[i][j],為 Si 到 Sj 是否為回文,true 說明 Si 到 Sj 是回文字符串
則有,P[i,j] = (P[i + 1, j - 1] && Si ==Sj)
初始條件p[i, i] = true, p[i,i+1] = Si==Si+1
動態規划的思想是首先判斷相鄰的字符串是否是回文,然后繼續判斷連續的三個字符是否是回文,然后是四個,…,直到判斷完整個字符串
時間復雜度O(n2),空間復雜度O(n2)
代碼實現:

  1. publicstaticString longestPalindromeDP(String str){
  2. if(str ==null|| str.length()<=0)returnnull;
  3. int len = str.length();
  4. int startIndex =0;
  5. int maxLen =1;
  6. boolean[][] p =newboolean[len][len];
  7. for(int i =0; i < len; i++){
  8. for(int j =0; j < len; j++){
  9. if(i == j){
  10. p[i][j]=true;
  11. continue;
  12. }
  13. p[i][j]=false;
  14. }
  15. }
  16. for(int i =0; i < len -1; i++){
  17. //相鄰的相同
  18. if(str.charAt(i)== str.charAt(i+1)){
  19. p[i][i+1]=true;
  20. startIndex = i;
  21. maxLen =2;
  22. }
  23. }
  24. for(int i =3; i <= len; i++){
  25. for(int j =0; j < len - i +1; j++){
  26. //當前判斷回文長度為i,起始位置為j
  27. int currLast = j + i -1;
  28. if(str.charAt(j)== str.charAt(currLast)&& p[j+1][currLast-1]){
  29. p[j][currLast]=true;
  30. startIndex = j;
  31. maxLen = i;
  32. }
  33. }
  34. }
  35. return str.substring(startIndex, startIndex+maxLen);
  36. }

二、中心檢測法

回文字符串的特點是以中心對稱,從0開始依次遍歷字符串,每次以選取的點為中心,向兩邊檢測,判斷是否符合回文字符串。
時間復雜度O(n2),空間復雜度O(1)

  1. publicstaticString longestPalindromeCerter(String str){
  2. if(str ==null|| str.length()<=0)returnnull;
  3. String longest = str.substring(0,1);
  4. for(int i =0; i < str.length()-1; i++){
  5. //獲得以i為中心的回文字符串
  6. String s = getPalindromeCerter(str, i, i);
  7. if(s.length()> longest.length()){
  8. longest = s;
  9. }
  10. //獲得以i和i+1為中心的回文字符串
  11. s = getPalindromeCerter(str, i, i +1);
  12. if(s.length()> longest.length()){
  13. longest = s;
  14. }
  15. }
  16. return longest;
  17. }
  18. //獲得以i,j為中心的回文字符串i==j時,就是以i為中心的回文字符串
  19. privatestaticString getPalindromeCerter(String str,int i,int j){
  20. while(i >=0&& j < str.length()&& str.charAt(i)== str.charAt(j)){
  21. i --;
  22. j ++;
  23. }
  24. return str.substring(i +1, j);
  25. }

三、添加輔助標志

首先我們把字符串S改造一下變成T,改造方法是:在S的每個字符之間和S首尾都插入一個”#”。這樣做的理由你很快就會知道。

例如,S=”abaaba”,那么T=”#a#b#a#a#b#a#”。

想一下,你必須在以Ti為中心左右擴展才能確定以Ti為中心的回文長度d到底是多少。(就是說這一步是無法避免的)
為了改進最壞的情況,我們把各個Ti處的回文半徑存儲到數組P,用P[i]表示以Ti為中心的回文長度。那么當我們求出所有的P[i],取其中最大值就能找到最長回文子串了。

對於上文的示例,我們先直接寫出所有的P研究一下。
i = 0 1 2 3 4 5 6 7 8 9 A B C
T = # a # b # a # a # b # a #
P = 0 1 0 3 0 1 6 1 0 3 0 1 0

顯然最長子串就是以P[6]為中心的”abaaba”。

你是否發現了,在插入”#”后,長度為奇數和偶數的回文都可以優雅地處理了?這就是其用處。

現在,想象你在”abaaba”中心畫一道豎線,你是否注意到數組P圍繞此豎線是中心對稱的?再試試”aba”的中心,P圍繞此中心也是對稱的。這當然不是巧合,而是在某個條件下的必然規律。我們將利用此規律減少對數組P中某些元素的重復計算。

我們來看一個重疊得更典型的例子,即S=”babcbabcbaccba”。

上圖展示了把S轉換為T的樣子。假設你已經算出了一部分P。豎實線表示回文”abcbabcba”的中心C,兩個虛實線表示其左右邊界L和R。你下一步要計算P[i],i圍繞C的對稱點是i’。有辦法高效地計算P[i]嗎?

我們先看一下i圍繞C的對稱點i’(此時i’=9)。

據上圖所示,很明顯P[i]=P[i’]=1。這是因為i和i’圍繞C對稱。同理,P[12]=P[10]=0,P[14]=P[8]=0。

現在再看i=15處。此時P[15]=P[7]=7?錯了,你逐個字符檢測一下會發現此時P[15]應該是5。

為什么此時規則變了?

如上圖所示,兩條綠色實線划定的范圍必定是對稱的,兩條綠色虛線划定的范圍必定也是對稱的。此時請注意P[i’]=7,超過了左邊界L。超出的部分就不對稱了。此時我們只知道P[i]>=5,至於P[i]還能否擴展,只有通過逐個字符檢測才能判定了。

在此例中,P[21]≠P[9],所以P[i]=P[15]=5。

我們總結一下上述分析過程,就是這個算法的關鍵部分了。
if P[ i’ ] < R – i,
then P[ i ] ← P[ i’ ]
else P[ i ] ≥ R - i. (此時要穿過R逐個字符判定P[i]).

很明顯C的位置也是需要移動的,這個很容易:
如果i處的回文超過了R,那么就C=i,同時相應改變L和R即可。

每次求P[i],都有兩種可能。如果P[i‘] < R – i,我們就P[i] = P[i’]。否則,就從R開始逐個字符求P[i],並更新C及其R。此時擴展R(逐個字符求P[i])最多用N步,而求每個C也總共需要N步。所以時間復雜度是2*N,即O(N)。
時間復雜度O(n),空間復雜度O(n)

實現:

  1. /**
  2. * 通過添加輔助標識,來獲得最長回文子串
  3. * @param str
  4. * @return
  5. */
  6. publicstaticString longestPalindromeAddTag(String str){
  7. if(str ==null|| str.length()<=0)returnnull;
  8. StringBuilder sb = addTag(str);
  9. int[] p =newint[sb.length()];//以i為中心的,左右半邊的回文子串長度(包括#)
  10. p[0]= p[sb.length()-1]=0;
  11. int center =0;int r =0;
  12. for(int i =1; i < sb.length()-1; i++){
  13. int i_mirror = center -( i - center);
  14. int diff = r - i;
  15. if(i_mirror>=0){
  16. if(p[i_mirror]< diff) p[i]= p[i_mirror];
  17. else{
  18. center = i;
  19. p[i]= diff;
  20. int pre = i - p[i]-1;//往前
  21. int after = i + p[i]+1;//往后
  22. while(pre >=0&& after < sb.length()&&
  23. sb.charAt(pre)== sb.charAt(after)){
  24. p[i]++;
  25. pre --;//往前
  26. after ++;//往后
  27. }
  28. r = i + p[i];//當前中心的右邊緣
  29. }
  30. }else{
  31. center = i;
  32. p[i]=0;
  33. int pre = i - p[i]-1;//往前
  34. int after = i + p[i]+1;//往后
  35. while(pre >=0&& after < sb.length()&&
  36. sb.charAt(pre)== sb.charAt(after)){
  37. p[i]++;
  38. pre --;//往前
  39. after ++;//往后
  40. }
  41. r = i + p[i];//當前中心的右邊緣
  42. }
  43. }
  44. int maxLen =0;
  45. int index =0;
  46. for(int i =0; i < sb.length(); i++){
  47. if(p[i]> maxLen){
  48. maxLen = p[i];
  49. index = i;
  50. }
  51. }
  52. int start =(index >>1)-(maxLen >>1);
  53. intlast=(index >>1)+(maxLen >>1);
  54. if((index &0x01)==1)last++;
  55. return str.substring(start,last);
  56. }
  57. /**
  58. * 向字符串中插入#,如 ab,則返回 #a#b#
  59. * @param str
  60. * @return
  61. */
  62. privatestaticStringBuilder addTag(String str){
  63. StringBuilder sb =newStringBuilder();
  64. sb.append('#');
  65. for(int i =0; i < str.length(); i++){
  66. sb.append(str.charAt(i));
  67. sb.append('#');
  68. }
  69. return sb;
  70. }

四、字符串變成回文字符串需要添加的字符數

  1. f(i,j)表示s[i..j]變為回文串需要添加的最少字符數。
  2. f(i,j)=0if i>=j
  3. f(i,j)=f(i+1,j-1)if i<j and s[i]==s[j]
  4. f(i,j)=min(f(i,j-1),f(i+1,j))+1if i<j and s[i]!=s[j]

實現:

  1. /**
  2. * 添加多少字符串使字符串變為回文串
  3. * @param str
  4. * @return
  5. */
  6. publicstaticint addTobePalindrome(String str){
  7. if(str ==null|| str.length()<=0)return0;
  8. int len = str.length();
  9. int[][] p =newint[len][len];
  10. for(int i =0; i < len; i++){
  11. for(int j =0; j < len; j++){
  12. p[i][j]=0;
  13. }
  14. }
  15. for(int i =2; i <= len; i++){
  16. for(int j =0; j < len - i +1; j++){
  17. int currLast = j + i -1;
  18. if(str.charAt(j)== str.charAt(currLast)){//判斷s[i...j]需要添加的字符個數
  19. p[j][currLast]= p[j+1][currLast-1];
  20. }else{
  21. p[j][currLast]=1+(p[j][currLast -1]< p[j+1][currLast]? p[j][currLast -1]: p[j+1][currLast]);
  22. }
  23. }
  24. }
  25. return p[0][len-1];
  26. }

參考:
[1]http://www.cnblogs.com/bitzhuwei/p/Longest-Palindromic-Substring-Par-I.html
[2]http://www.cnblogs.com/bitzhuwei/p/Longest-Palindromic-Substring-Part-II.html






免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM