BWT (Burrows–Wheeler_transform)資料轉換演算法
1.什麼是BWT
壓縮技術主要的工作方式就是找到重複的模式,進行緊密的編碼。
BWT(Burrows–Wheeler_transform)將原來的文字轉換為一個相似的文字,轉換後使得相同的字元位置連續或者相鄰,之後可以使用其他技術如: 和 遊程編碼 進行文字壓縮。
2.BWT原理
2.1 BWT編碼
(1)首先,BWT先對需要轉換的文字塊,進行迴圈右移,每次迴圈一位。可以知道長度為n的文字塊,迴圈n次後重複,這樣就得到看n個長度為n的字串。如下圖中的“Rotate Right”列。(其中‘#’作為識別符號,不在文字塊的字符集中,這樣保證n個迴圈移位後的字串均布相同。並且定義'#'小於字符集中的任意字元)。
(2)對迴圈移位後的n個字串按照字典序排序。如下圖中的“Sorted (M)”列。
(3)記錄下“Sorted (M)”列中每個字串的最後一個字元,組成了“L”列。(其中"F"列是“Sorted (M)”列中每個字串的字首)
這樣,原來的字串“banana#”就轉換為了“annb#aa”。在某些情況下,使用L列進行壓縮會有更好的效果。“L”列就是編碼的結果。
2.2 BWT解碼
因為進行的是迴圈移位,且是迴圈左移注意下面的性質:
1、L的第一個元素是Text中的最後一個元素
2、對於M中的每一行(第一行除外)第一個元素都是最後一個元素的下一個元素。
也就是說,對於文字塊而言,同一行中F是L的下一個元素,L是F的前一個元素。
這樣,就需要
(1)通過"F"列中的元素,找到他前面的字元,就是對應的同一行“L”列;
(2)通過“L”列中的元素,找到他在“F”列中的對應字元位置。但是“L”中有3個字元a,如何對應F中的3個a呢?因為L是F的前一個元素,多個具有相同字首的字串排序,去掉共同字首後相對次序沒有變化。所有遇到多個相同的字元,相對位置不變;
(3)轉到(1),直到結束。
因為F列是已經排序的,可以從L列獲得,所有隻需要儲存L列就可以。從L列中的字元獲取在F列中的位置時,需要:
(1)字首和陣列,記錄小於當前字元的字元數個數。
(2)count計數,計算L中從開始位置到當前字元位置等於該字元的字元數。(保證多個相同字元下"L"到“F”的相對位置不變)。
3.BWT文字塊編碼、解碼例項
1 #include <iostream> 2 #include <string> 3 #include <algorithm> 4 #include <string.h> 5 using namespace std; 6 7 ///編碼,生成last陣列 8 int getLastArray(char *lastArray,const string &str){ ///子串排序 9 int len=str.size(); 10 string array[len]; 11 12 for(int i=0;i<len;i++){ 13 array[i] = str.substr(i); 14 } 15 sort(array,array+len); 16 for(int i=0;i<len;i++){ 17 lastArray[i] = str.at((2*len-array[i].size()-1)%len); 18 } 19 return 0; 20 } 21 22 int getCountPreSum(int *preSum,const string &str){ 23 memset(preSum,0,27*sizeof(int)); 24 for(int i=0;i<str.size();i++){ 25 if(str.at(i) == '#') 26 preSum[0]++; 27 else 28 preSum[str.at(i)-'a'+1]++; 29 } 30 31 for(int i=1;i<27;i++) 32 preSum[i] += preSum[i-1]; 33 return 0; 34 } 35 36 ///解碼,使用last陣列,恢復原來的文字塊 37 int regainTextFromLastArray(char *lastArray,char *reGainStr,int *preSum){ 38 int len=strlen(lastArray); 39 int pos=0; 40 char c; 41 for(int i=len-1;i>=0;){ 42 reGainStr[i] = lastArray[pos]; 43 c = lastArray[pos]; 44 pos = preSum[c-'a']+count(lastArray,lastArray+pos,c); 45 i--; 46 } 47 return 0; 48 } 49 50 int main (){ 51 string str("sdfsfdfdsdfgdfgfgfggfgdgfgd#"); 52 int preSum[27]; 53 int len=str.size(); 54 55 char *lastArray = new char[len+1]; 56 char *reGainStr = new char[len+1]; 57 lastArray[len]='\0'; 58 reGainStr[len]='\0'; 59 60 getCountPreSum(preSum,str); 61 getLastArray(lastArray,str); 62 regainTextFromLastArray(lastArray,reGainStr,preSum); 63 64 cout<<" str: "<<str<<endl; 65 cout<<"lastArray : "<<lastArray<<endl; 66 cout<<"reGainStr : "<<reGainStr<<endl; 67 68 delete lastArray; 69 delete reGainStr; 70 return 0; 71 }
程式碼執行輸出:
參考: