最長迴文子序列

在今年元旦,買了《算法導論》第三版,其實早在它還沒有出版的時候,我就已經關注了,哈哈,於是就迫不及待地讀了起來,儘管是在考試周。不過之後擱置了一段時間,這個月又開始讀起來了。看到課後思考題15-2,231頁,就寫了一下。最長迴文子序列,其具體問題如下:

迴文是正序與逆序相同的非空字符,像civic,racecar等,還包括長度是1的字符串。求給定輸入字符串的最長迴文子序列。例如,給定輸入character,算法返回carac。

假如給定字符是string,其長度爲n,設定指針i、j分別指向string的頭和尾,即是0<=i<j<n,初始時i=0,j=n-1。

解決方法如下:從字符串末尾開始判斷是否有字符與當前指針i指向的string[i]相等,即判斷string[i]與string[j]是否相等:

  1. 如果不相等,則指針j向前挪一位,即j=j-1,直到j>=i,此時還不相等,則i向後移動,即i=i+1,該過程中始終保持i<j
  2. 如果某位置的string[i]與string[j]相等,則參照公式,公式中遞歸定義了子問題的解:

p[i,j] = 1,如果j-i<2,又因爲j>i,也就是j-i=1;

p[i,j] = 3,如果j-i=2;

p[i,j] = p[i+1,j-1]+2,如果j-i>2;

其中p[i,j]表示字符串string中以第i個字符開始、第j個字符結束的串的最長迴文子序列長度,當j-i=1時,說明字符串中只有兩個字符,而這兩個字符又相等,明顯p[i,j]應該爲1;當j-i=2時,說明字符串有3個字符,兩個相等的字符中夾了一個其他字符,明顯p[i,j]也爲2+1=3;當j-i>2時,則利用子問題的解來遞歸定義此時問題的解。

其實,這個問題使用了動態規劃的解題方法,找出問題的最優子結構性質,在利用子問題來遞歸定義原問題(使用子問題來表示原問題的解),把解決原問題的解建立在子問題的基礎之上。

在實現動態規劃的算法時,通常有2種實現思路,一是自底向上,也就是先求解小的子問題,然後慢慢擴大子問題的規模再求解,而求解規模較大的子問題時有需要使用到規模較小的子問題,所以說是自底向上,不過要仔細考慮具體實現時的順序;而是帶備忘的自定向下的遞歸方法,與一般遞歸方法不同的是,如果不用重複求解已經解過的子問題,只要直接查表即可。

其中在求解最優化問題的時候,通常涉及到構造最優解,此處是使用數組mark記錄下在比較過程中相等字符的位置,分別是i,j。

實現代碼如下,這是帶備忘的自定向下的遞歸實現:

  1. #include <string> 
  2. using namespace std; 
  3.  
  4. size_t huiwen_length(string &x,size_t i,size_t j); 
  5. void print_huiwen(string &x,size_t * mark); 
  6.  
  7. size_t p[20][20]; 
  8. size_t mark[20]; 
  9. void main(int argc, char **argv){ 
  10.     string x("tccaivic"); 
  11.     huiwen_length(x,0,x.size()-1); 
  12.     print_huiwen(x,mark); 
  13.  
  14. size_t huiwen_length(string &x,size_t i,size_t j){ 
  15.     if (p[i][j]>0) 
  16.     { 
  17.         return p[i][j]; 
  18.     } 
  19.      
  20.     for (size_t begin=i,end=j;i<j;i++,j=end;) 
  21.     { 
  22.         for (;i<j;) 
  23.         { 
  24.             if (x[i]==x[j]) 
  25.             { 
  26.                 if (j-i>2)//j-i>2 
  27.                 { 
  28.                     p[begin][end]=huiwen_length(x,i+1,j-1)+2;//p[i][j] = p[i+1][j-1]+2; 
  29.  
  30.                 }else if (j-i==2)//j-i==2相當於相等的2個元素之間夾了一個元素 
  31.                 { 
  32.                     p[begin][end]=3; 
  33.                     mark[i+1]=1;//設置夾了那個 
  34.                     //return 3;//p[i][j]=3; 
  35.                 }else {//0<j-i<2,即是j-i==1 
  36.                     p[begin][end]=2; 
  37.                     //return 2;//p[i][j]=2; 
  38.                 } 
  39.                 mark[i]=1; 
  40.                 mark[j]=1; 
  41.                 return p[begin][end]; 
  42.             }else
  43.                 j--; 
  44.             }        
  45.         } 
  46.     } 
  47.     return 0; 
  48.  
  49. void print_huiwen(string &x,size_t * mark){ 
  50.     cout<<"huiwen of "<<x<<":"
  51.     for (size_t i=0;i<x.size();i++) 
  52.     { 
  53.         if (mark[i]==1) 
  54.         { 
  55.             cout<<x[i]; 
  56.         } 
  57.     } 
  58.     cout<<endl; 

動態規劃適用於求解最優化問題,掌握了該方法可以更好地增強自己的算法能力。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章