什么是Burrows-Wheeler变换?
BWT是一种数据转换算法,它以使转换后的消息更具可压缩性的方式重组数据。从技术上讲,它是字符串字符的字典顺序可逆排列。在实现构成Unix压缩实用程序bzip2的基础的Burrows-Wheeler数据压缩算法时,这是要连续执行的三个步骤中的第一步。
为什么选择BWT?其背后的主要思想。
BWT的最重要应用是在生物科学中,其中基因组(用A,C,T,G字母写的长字符串)运行次数不多,但它们却有很多重复。
BWT的想法是构建一个数组,该数组的行都是字典顺序的输入字符串的循环移位,并返回该数组的最后一列,该列往往具有相同的字符。这样做的好处是,一旦字符被聚在一起,它们就会有效地进行排序,这可以使我们的字符串对于其他算法(如游程长度编码和霍夫曼编码)具有更高的可压缩性。
BWT的显着之处在于,这种特定的转换是可逆的,并且数据开销最小。
BWT算法涉及的步骤
让我们以“ banana $”一词为例。
- 步骤1:形成给定文本的所有循环旋转。
banana$ $ b $banana a a a$banan Cyclic rotations ----------> na$bana n n ana$ban a nana$ba anana$b
- 步骤2:下一步是按字典顺序对旋转进行排序。在字典上, “ $”符号被视为第一个字母,甚至在“ a”之前。
banana$ $banana $banana a$banan a$banan Sorting ana$ban na$bana ----------> anana$b ana$ban alphabetically banana$ nana$ba na$bana anana$b nana$ba
- 步骤3:最后一列是我们作为BWT输出的内容。
BWT(banana$) = annb$aa
例子:
Input: text = “banana$”
Output: Burrows-Wheeler Transform = “annb$aa”
Input: text = “abracadabra$”
Output: Burrows-Wheeler Transform = “ard$rcaaaabb”
为什么最后一列被视为BWT?
- 最后一列比其他任何列都有更好的符号聚类。
- 如果只有字符串的BWT,则可以完全恢复其余的循环旋转。其余的列不具有此特征,这在计算BWT的逆数时非常重要。
为什么在文本中嵌入“ $”符号?
即使我们的文本未与任何EOF字符(此处为“ $” )连接,我们也可以计算BWT。计算BWT的逆时会出现“ $”符号的含义。
实施方式
- 让我们实例化“ banana $”作为我们的input_text,并实例化字符数组bwt_arr作为我们的输出。
- 让我们获取“ banana $”的所有后缀,并计算它的suffix_arr来存储每个后缀的索引。
0 banana$ 6 $ 1 anana$ 5 a$ 2 nana$ Sorting 3 ana$ 3 ana$ ----------> 1 anana$ 4 na$ alphabetically 0 banana$ 5 a$ 4 na$ 6 $ 2 nana$
- 遍历suffix_arr ,现在将添加到输出数组bwt_arr ,它是每次旋转的最后一个字符。
- 可以使用input_text [(suffix_arr [i] – 1 + n)%n]来计算从后缀数组中当前值表示的位置开始的input_text每次旋转的最后一个字符,其中n是元素中元素的数量。 suffix_arr 。
bwt_arr[0] = input_text[(suffix_arr[0] - 1 + 7) % 7] = input_text[5] = a bwt_arr[1] = input_text[(suffix_arr[1] - 1 + 7) % 7] = input_text[4] = n
以下是上述实现方式的代码
// C program to find Burrows Wheeler transform
// of a given text
#include
#include
#include
// Structure to store data of a rotation
struct rotation {
int index;
char* suffix;
};
// Compares the rotations and
// sorts the rotations alphabetically
int cmpfunc(const void* x, const void* y)
{
struct rotation* rx = (struct rotation*)x;
struct rotation* ry = (struct rotation*)y;
return strcmp(rx->suffix, ry->suffix);
}
// Takes text to be transformed and its length as
// arguments and returns the corresponding suffix array
int* computeSuffixArray(char* input_text, int len_text)
{
// Array of structures to store rotations and
// their indexes
struct rotation suff[len_text];
// Structure is needed to maintain old indexes of
// rotations after sorting them
for (int i = 0; i < len_text; i++) {
suff[i].index = i;
suff[i].suffix = (input_text + i);
}
// Sorts rotations using comparison
// function defined above
qsort(suff, len_text, sizeof(struct rotation),
cmpfunc);
// Stores the indexes of sorted rotations
int* suffix_arr
= (int*)malloc(len_text * sizeof(int));
for (int i = 0; i < len_text; i++)
suffix_arr[i] = suff[i].index;
// Returns the computed suffix array
return suffix_arr;
}
// Takes suffix array and its size
// as arguments and returns the
// Burrows - Wheeler Transform of given text
char* findLastChar(char* input_text,
int* suffix_arr, int n)
{
// Iterates over the suffix array to find
// the last char of each cyclic rotation
char* bwt_arr = (char*)malloc(n * sizeof(char));
int i;
for (i = 0; i < n; i++) {
// Computes the last char which is given by
// input_text[(suffix_arr[i] + n - 1) % n]
int j = suffix_arr[i] - 1;
if (j < 0)
j = j + n;
bwt_arr[i] = input_text[j];
}
bwt_arr[i] = '\0';
// Returns the computed Burrows - Wheeler Transform
return bwt_arr;
}
// Driver program to test functions above
int main()
{
char input_text[] = "banana$";
int len_text = strlen(input_text);
// Computes the suffix array of our text
int* suffix_arr
= computeSuffixArray(input_text, len_text);
// Adds to the output array the last char
// of each rotation
char* bwt_arr
= findLastChar(input_text, suffix_arr, len_text);
printf("Input text : %s\n", input_text);
printf("Burrows - Wheeler Transform : %s\n",
bwt_arr);
return 0;
}
Input text : banana$
Burrows - Wheeler Transform : annb$aa
时间复杂度: O( 登录)。这是因为上面使用的方法来构建具有O( 由于O(nLogn)排序算法中的字符串比较需要O(n)时间,因此Logn)时间复杂度高。
锻炼:
- 在O(nLogn)时间计算后缀数组,然后实现BWT。
- 实现Burrows-Wheeler变换的逆函数。