日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

【串和序列處理 6】LCS 最長公共子序列

 靜聽沙漏 2012-01-08

LCS:又稱最長公共子序列。其中子序列(subsequence)的概念不同于串的子串。它是一個不一定連續(xù)但按順序取自字符串X中的字符序列。例如:串"AAAG"就是串“CGATAATTGAGA”的一個子序列。

字符串的相似性問題可以通過求解兩個串間的最長公共子序列(LCS)來得到。當(dāng)然如果使用窮舉算法列出串的所有子序列,一共有2^n種,而每個子序列是否是另外一個串的子序列又需要O(m)的時間復(fù)雜度,因此這個窮舉的方法時間復(fù)雜度是O(m*(2^n))指數(shù)級別,效率相當(dāng)?shù)目膳?。我們采用動態(tài)規(guī)劃算法來解決這個問題。

動態(tài)規(guī)劃算法解決最長公共子序列

假如我們有兩個字符串:X=[0,1,2....n] Y=[0,1,2...m]。我們定義L(i, j)為X[0...i]與Y[0...j]之間的最長公共子序列的長度。通過動態(tài)規(guī)劃思想(復(fù)雜問題的最優(yōu)解是子問題的最優(yōu)解和子問題的重疊性質(zhì)決定的)。我們考慮這樣兩種情況:

(1) 當(dāng)X[i]=Y[j]時, L(i, j)=L(i-1, j-1)+1。證明很簡單。

(2) 當(dāng)X[i]!=Y[j]時,說明此事X[0...i]和Y[0...j]的最長公共子序列中絕對不可能同時含有X[i]和Y[j]。那么公共子序列可能以X[i]結(jié)尾,可能以Y[j]結(jié)尾,可以末尾都不含有X[i]或Y[j]。因此

L(i, j)= MAX{L(i-1 , j), L(i, j-1)}

LCS動態(tài)規(guī)劃Java源代碼

Java代碼
  1. package net.hr.algorithm.string;
  2. /**
  3. * 最長公共子串LCS
  4. * @author heartraid
  5. */
  6. public class LCS {
  7. /**字符串X的字符數(shù)組*/
  8. private char[] charArrayX=null;
  9. /**字符串Y的字符數(shù)組*/
  10. private char[] charArrayY=null;
  11. public LCS(String sa,String sb){
  12. charArrayX=new char[sa.length()+1];
  13. System.arraycopy(sa.toCharArray(),0,charArrayX,1,sa.length());
  14. charArrayY=new char[sb.length()+1];
  15. System.arraycopy(sb.toCharArray(),0,charArrayY,1,sb.length());
  16. }
  17. /**
  18. * 得到最長公共子序列的長度
  19. */
  20. public void getLCS(){
  21. int[][] length=new int[charArrayX.length+1][charArrayY.length+1];
  22. for(int m=1;m<charArrayX.length;m++)
  23. for(int n=1;n<charArrayY.length;n++){
  24. if(charArrayX[m]==charArrayY[n]){
  25. length[m][n]=length[m-1][n-1]+1;
  26. }
  27. else
  28. length[m][n]=max(length[m-1][n],length[m][n-1]);
  29. }
  30. //打印最長公共子序列
  31. String lcstr="";
  32. int x=charArrayX.length-1;
  33. int y=charArrayY.length-1;
  34. while(x>=1&&y>=1){
  35. if(charArrayX[x]==charArrayY[y]){
  36. lcstr=charArrayX[x]+lcstr;
  37. x--;
  38. y--;
  39. }else{
  40. if(length[x-1][y]<=length[x][y-1])
  41. y--;
  42. else
  43. x--;
  44. }
  45. }
  46. System.out.println("最長公共子序列為:"+lcstr+" [length="+lcstr.length()+"]");
  47. }
  48. /**
  49. * 取最大值
  50. */
  51. private int max(int m,int n){
  52. return m>n?m:n;
  53. }
  54. /**
  55. * 測試
  56. */
  57. public static void main(String[] args) {
  58. LCS lcs=new LCS("GTTCCTAATA","CGATAATTGAGA");
  59. lcs.getLCS();
  60. }
  61. }

這里解釋一下上面的代碼,其中g(shù)etLength()的作用是遞歸獲取最長公共子串的長度,并得到遞歸過程中每個子串之間最長公共子串長度的狀態(tài)表lcs[][],這張表運(yùn)行的結(jié)果如下:

C G A T A A T T G A G A

0 0 0 0 0 0 0 0 0 0 0 0 0
G 0 0 1 1 1 1 1 1 1 1 1 1 1
T 0 0 1 1 2 2 2 2 2 2 2 2 2
T 0 0 1 1 2 2 2 3 3 3 3 3 3
C 0 1 1 1 2 2 2 3 3 3 3 3 3
C 0 1 1 1 2 2 2 3 3 3 3 3 3
T 0 1 1 1 2 2 2 3 4 4 4 4 4
A 0 1 1 2 2 3 3 3 4 4 5 5 5
A 0 1 1 2 2 3 4 4 4 4 5 5 6
T 0 1 1 2 3 3 4 5 5 5 5 5 6
A 0 1 1 2 3 4 4 5 5 5 6 6 6

紅色數(shù)字的位置就揭示了最長公共子序列: CTATTA。也就是說分析這張表就可以得到最長公共子序列字符串。

為什么呢?因為通過表的回溯過程,從后向前重構(gòu)了一個最長公共子序列。對于任何位置lcs[i][j],確定是否X[i]=Y[j]。如果是,那么X[i]必是最長公共子序列的一個字符。如果否,那么移動到lcs[i,j-1]和lcs[i-1, j]之間的較大者。

動態(tài)規(guī)劃方法LCS效率:

動態(tài)規(guī)劃方法構(gòu)造最長公共子序列需要O(m*n)的代價,另外,如果想要得到最長公共子序列,又需要O(m+n)的時間來讀取csl[][]數(shù)組。盡管如此,其時間復(fù)雜度仍然比蠻力窮舉的指數(shù)級別要強(qiáng)的多。

問題拓展:設(shè)A,B,C是三個長為n的字符串,它們?nèi)∽酝怀?shù)大小的字母表。設(shè)計一個找出三個串的最長公共子串的O(n^3)的時間算法。(來自《Algorithm Design》(中文版:算法分析與設(shè)計) - Chapter9 - 文本處理 - 創(chuàng)新題C-9.18)

分析:可以通過《LCS最長公共子序列》的動態(tài)規(guī)劃算法,設(shè)計Java源代碼如下:

Java代碼 復(fù)制代碼 收藏代碼
  1. public class TriLCS{
  2. char[] charA=null;
  3. char[] charB=null;
  4. char[] charC=null;
  5. public TriLCS(String sa,String sb,String sc){
  6. charA=new char[sa.length()+1];
  7. System.arraycopy(sa.toCharArray(),0,charA,1,sa.length());
  8. charB=new char[sb.length()+1];
  9. System.arraycopy(sb.toCharArray(),0,charB,1,sb.length());
  10. charC=new char[sc.length()+1];
  11. System.arraycopy(sc.toCharArray(),0,charC,1,sc.length());
  12. }
  13. public void getTriLCS(){
  14. int[][][] length=new int[charA.length][charB.length][charC.length];
  15. for(int a=1;a<charA.length;a++)
  16. for(int b=1;b<charB.length;b++)
  17. for(int c=1;c<charC.length;c++){
  18. if(charA[a]==charB[b]&&charA[a]==charC[c]){
  19. length[a][b][c]=length[a-1][b-1][c-1]+1;
  20. }
  21. else if(charA[a]==charB[b]&&charA[a]!=charC[c]){
  22. length[a][b][c]=max(length[a-1][b-1][c],length[a][b][c-1]);
  23. }
  24. else if(charA[a]==charC[c]&&charA[a]!=charB[b]){
  25. length[a][b][c]=max(length[a-1][b][c-1],length[a][b-1][c]);
  26. }
  27. else if(charB[b]==charC[c]&&charA[a]!=charB[b]){
  28. length[a][b][c]=max(length[a][b-1][c-1],length[a-1][b][c]);
  29. }
  30. else{
  31. length[a][b][c]=max(length[a-1][b][c],length[a][b-1][c],length[a][b][c-1],length[a-1][b-1][c],length[a-1][b][c-1],length[a][b-1][c-1]);
  32. }
  33. }
  34. //打印最長公共子序列
  35. String lcstr="";
  36. int a=charA.length-1;
  37. int b=charB.length-1;
  38. int c=charC.length-1;
  39. while(a>=1&&b>=1&&c>=1){
  40. if(charA[a]==charB[b]&&charA[a]==charC[c]){
  41. lcstr=charA[a]+lcstr;
  42. a--;
  43. b--;
  44. c--;
  45. }
  46. else if(charA[a]==charB[b]&&charA[a]!=charC[c]){
  47. if(length[a-1][b-1][c]<=length[a][b][c-1])
  48. c--;
  49. else{
  50. a--;
  51. b--;
  52. }
  53. }
  54. else if(charA[a]==charC[c]&&charA[a]!=charB[b]){
  55. if(length[a-1][b][c-1]<=length[a][b-1][c])
  56. b--;
  57. else{
  58. a--;
  59. c--;
  60. }
  61. }
  62. else if(charB[b]==charC[c]&&charA[a]!=charB[b]){
  63. if(length[a][b-1][c-1]<=length[a-1][b][c])
  64. a--;
  65. else{
  66. b--;
  67. c--;
  68. }
  69. }
  70. else{
  71. int maxize=max(length[a-1][b][c],length[a][b-1][c],length[a][b][c-1],length[a-1][b-1][c],length[a-1][b][c-1],length[a][b-1][c-1]);
  72. if(maxize==length[a-1][b][c])
  73. a--;
  74. if(maxize==length[a][b-1][c])
  75. b--;
  76. if(maxize==length[a][b][c-1])
  77. c--;
  78. if(maxize==length[a-1][b-1][c]){
  79. a--;
  80. b--;
  81. }
  82. if(maxize==length[a-1][b][c-1]){
  83. a--;
  84. c--;
  85. }
  86. if(maxize==length[a][b-1][c-1]){
  87. b--;
  88. c--;
  89. }
  90. }
  91. }
  92. System.out.println("最長子串為:"+lcstr+"(length="+length[charA.length-1][charB.length-1][charC.length-1]+")");
  93. }
  94. /**
  95. * 取最大值
  96. */
  97. private int max(int m,int n){
  98. return m>n?m:n;
  99. }
  100. /**
  101. * 取最大值
  102. */
  103. private int max(int x,int y,int z,int k,int m,int n){
  104. int maxizen=0;
  105. if(maxizen<x) maxizen=x;
  106. if(maxizen<y) maxizen=y;
  107. if(maxizen<z) maxizen=z;
  108. if(maxizen<k) maxizen=k;
  109. if(maxizen<m) maxizen=m;
  110. if(maxizen<n) maxizen=n;
  111. return maxizen;
  112. }
  113. public static void main(String[] args){
  114. TriLCS tri=new TriLCS("aadsbbbcs","adsabcs","adbsbsdcs");
  115. tri.getTriLCS();
  116. }
  117. }

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多