利用尾碼數組(suffix array)求最長公用子串(longest common substring)

來源:互聯網
上載者:User

標籤:style   blog   http   color   os   io   使用   strong   ar   

  摘要:本文討論了最長公用子串的的相關演算法的時間複雜度,然後在尾碼數組的基礎上提出了一個時間複雜度為o(n^2*logn),空間複雜度為o(n)的演算法。該演算法雖然不及動態規劃和尾碼樹演算法的複雜度低,但其重要的優勢在於可以編碼簡單,代碼易於理解,適合快速實現。

 

  首先,來說明一下,LCS通常指的是公用最長子序列(Longest Common Subsequence,名稱來源參見《演算法導論》原書第3版p223),而不是公用最長子串(也稱為最長公用子串)。

  最長公用子串問題是在文本串、模式串中尋找共有的一個最長的子串,如文本串text=“abcbcedf”,pattern=“ebcbcdf”,則最長公用子串為“bcbc”,長度為4。

  最長公用子串的解法很多,有蠻力搜尋法、動態規劃法、尾碼數組法、尾碼樹法。本文著重提尾碼數組法,其他方法可以自行百度。

  蠻力搜尋法

  

 1 int enum_longestCommonSubstring(char *text,char *pattern) 2  { 3     if(!text || !pattern)  return 0;     //nullptr 4     int tlen=strlen(text),plen=strlen(pattern); 5     if(0==tlen || 0==plen) return 0; //empty string 6     int maxLEN=0,i=0,j=0,ofs=0; 7     for(i=0;i<tlen && (tlen-i>=maxLEN);++i) 8         for(j=0;j<plen && (plen-j>=maxLEN); ++j) 9             if( *(text+i)==*(pattern+j) )10             {    11                 ofs=1;12                 while((i+ofs)<tlen&&(j+ofs)<plen&&*(text+ofs)==*(pattern+ofs))13                     {    ++ofs;   }14                 if(ofs>maxLEN)  maxLEN=ofs;  //update15             }16     return maxLEN;17 }

  記文本串長度為m,模式串長度為n,則暴力搜尋法時間複雜度為o(m*n*Min(m,n)),空間複雜度o(1)。在子串匹配問題上,如果使用KMP演算法,則演算法效率可以提高。

  動態規劃

  動態規劃求解最長公用子串問題的時間複雜度為o(m*n),經過最佳化後的動態規划算法可以達到o(Min(m,n))的空間複雜度

  參見http://www.cnblogs.com/ider/p/longest-common-substring-problem-optimization.html

  

  尾碼數組

  利用排序後的尾碼數組(suffix array)來求解最長公用子串步驟為:

    一,拼接文本串和模式串得到一個新的串X;

    二,將X的所有尾碼數組存入sa;(文本串長度為m,模式串長n。步驟二時間複雜度o(m+n)

    三,對sa進行排序;

    四,計算sa中相鄰的子串的最長公用前置長度(時間複雜度o((m+n)*Min(m,n)))

    註:為了避免得到單個串的最長重複子串,在步驟四種參與比較的兩個子串應該為一個是文本串的子串,另一個為模式串的子串。因此,在步驟一、二中就應該附加記錄位來處理。

  《尾碼數組——————處理字串的有力工具處理字串的有力工具》羅穗騫介紹了使用基數排序來排序尾碼數組的方法,排序時間複雜度(m+n)*log(m+n)。因此,使用使用尾碼數組+基數排序得到的演算法的時間複雜度為o((m+n)*Min(m,n))(步驟四決定最大時間複雜度)。但是,該方法較複雜,不容易掌握,在此處,我提出一種尾碼數組+C標準庫sort排序的演算法,其排序時間複雜度為o(Min(m,n)*(m+n)*log(m+n)),因此,演算法整體的時間複雜度為o(Min(m,n)*(m+n)*log(m+n))(由步驟三決定最大時間複雜度),此外,該演算法空間複雜度為o(m+n)。  “尾碼數組+快排”演算法時間複雜低於“尾碼數組+基數排序”的時間複雜度,但優點在於利用標準庫sort+strcmp來實現排序,代碼簡單,演算法更容易理解。代碼如下:

  

 1 #include<stdio.h> 2 #include<iostream> 3 #include<string.h> 4 #include<algorithm> 5 using namesapce std; 6 int suffixArrayQsort_longestCommonSubstring(char *text,char *pattern) 7 { 8     if(!text || !pattern)  return 0;     //nullptr 9     int tlen=strlen(text),plen=strlen(pattern),i,j;10     if(0==tlen || 0==plen) return 0; //empty string11 12     enum ATTRIB{TEXT,PATTERN};13     struct absInfo14     {15         char *head;16         ATTRIB attr;  //tag17         int len;18         absInfo():head(NULL),attr(TEXT),len(0){}19         absInfo(char *phead,ATTRIB attrib,int length):head(phead),attr(attrib),len(length){}20         bool operator < (const absInfo &b)21         {22             return  strcmp(head,b.head)<0;23         }24         static void display(const absInfo &a)25         {26             printf("size:%d type:%-7s    ",a.len, (a.attr==TEXT?"TEXT":"PATTERN") );27             printf("%s\n",a.head);28         }29     }*sa;30 31     //step 2:build the suffix array32     sa=new absInfo[tlen+plen];33     for(i=0;i<tlen;++i)34     {35         sa[i].head=text+i;36         sa[i].attr=TEXT;37         sa[i].len=tlen-i;38     }39     for(j=0;j<plen;++j)40     {41         sa[j+tlen].head=pattern+j;42         sa[j+tlen].attr=PATTERN;43         sa[j+tlen].len=plen-j;44     }45 46     //step 3:use sort() to sort the sa47     puts("before sort, the sa is:"); for_each(sa,sa+tlen+plen,absInfo::display);48     sort(sa,sa+tlen+plen);49     puts("after sort, the sa is:"); for_each(sa,sa+tlen+plen,absInfo::display);50 51     //step 4:compare52     int maxLEN=0,rec=0;53     for(i=0;i<tlen+plen-1;i++)54     {55         if(sa[i].attr==sa[i+1].attr) continue;56         if(sa[i].len<=maxLEN || sa[i+1].len<=maxLEN) continue;57         rec=0;58         while(rec<sa[i].len && rec<sa[i+1].len && *(sa[i].head+rec)==*(sa[i+1].head+rec) )59           ++rec;60         if(rec>maxLEN)  maxLEN=rec; //update61     }62     //release memory resource and return63     delete [] sa; sa=NULL;64     return maxLEN;65 }

  註:1,absInfo結構中len欄位不是必須的,設定此欄位只是為了在代碼56行處做一個搜尋剪枝操作。

    2,稍微改動代碼就能在演算法中給出公用子串的值(對樣本來說就是給出“bcbc"),通過absInfo的len欄位和maxLEN值也可以在o(1)的時間複雜度內計算出公用子串分別在文本串和模式串中的位置

  運行結果:

  當文本串text=“abcbcedf”,pattern=“ebcbcdf”時,代碼運行如所示:

  

  從代碼可以看出,“尾碼數組+qsort排序”實現最長公用子串具有編碼簡單的特點,空間複雜度為o(m+n)

  尾碼樹

  尾碼樹以及廣義的尾碼樹演算法讀者可以自行搜尋。

  

利用尾碼數組(suffix array)求最長公用子串(longest common substring)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.