-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbwt.cpp
211 lines (189 loc) · 9.82 KB
/
bwt.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#include <string.h>
#include <algorithm>
#include "definitions.h"
using namespace std;
void suffixArrayBuild(const byte* in, int* permutation, int size);
int suffixArrayBuild_firstStep(const byte* in, int size, int* permutation, int* classesNumbers);
void countingSortPermutation(const byte* in, int size, int* permutation);
void countingSortPermutationOnSecondElementsOfPairs(const int* pn, int size, int* permutation,
const int* classesNumbers, int classes);
int BWTEncode(const byte* in, byte* out, int size)
{
int* suffix_array = new int[size];
suffixArrayBuild(in, suffix_array, size);
int lastBytePosition = 0;
for (int i=0; i<size; ++i)
{
if (suffix_array[i])
out[i] = in[suffix_array[i] - 1];
else
{
out[i] = in[size - 1];
lastBytePosition = i;
}
}
delete [] suffix_array;
return lastBytePosition;
}
void BWTDecode(const byte* in, byte* out, int size, int lastBytePosition)
{
// c[] для каждой буквы алфавита содержит количество раз,
// которое она встречается в тексте.
// Требуется инициализация нулями.
int c[ALPHABET];
memset(c, 0, ALPHABET * sizeof(int));
// d[] для каждой буквы алфавита содержит количество раз,
// которое встречаются все меньшие ее буквы.
// Требуется инициализация 0-го элемента нулем.
int d[ALPHABET];
memset(d, 0, ALPHABET * sizeof(int));
// p[] для каждого символа текста in содержит количество раз,
// которое этот символ встречается раньше по тексту.
int* p = new int[size];
// Заполняем с[], d[], и p[].
for (int i=0; i<size; ++i)
p[i] = c[in[i]]++;
for (int i=1; i<ALPHABET; ++i)
d[i] = d[i-1] + c[i-1];
// t[] - вектор перестановки.
int* t = new int[size];
for (int i=0; i<size; ++i)
t[i] = d[in[i]] + p[i];
delete [] p;
// Используя вектор перестановки, вычисляем все буквы текста от начала к концу.
for (int i=size-1; i>=0; --i)
{
out[i] = in[lastBytePosition];
lastBytePosition = t[lastBytePosition];
}
delete [] t;
}
/**
* Строит суффиксный массив permutation текста in алгоритмом O(NlogN)
* (применяя видоизмененную поразрядную сортировку к циклическим сдвигам)
*/
void suffixArrayBuild(const byte* in, int* permutation, int size)
{
// classesNumbers[i] для каждой циклической подстроки, начинающейся в позиции i с длиной 2^h,
// содержит номер класса эквивалентности, которому эта подстрока принадлежит.
int* classesNumbers = new int[size];
int classes = suffixArrayBuild_firstStep(in, size, permutation, classesNumbers);
// pn[] содержит перестановку по вторым элементам пар.
int* pn = new int[size];
// newClassesNumbers[] содержит номера новых классов эквивалентности.
int* newClassesNumbers = new int[size];
for (int h=0; (1<<h)<size; ++h)
{
// Сортировка по вторым элементам пар.
for (int i=0; i<size; ++i)
{
pn[i] = permutation[i] - (1<<h);
if (pn[i] < 0)
pn[i] += size;
}
// Стабильная сортировка подсчетом по первым элементам пар.
countingSortPermutationOnSecondElementsOfPairs(pn, size, permutation, classesNumbers, classes);
// Проходом по вектору перестановки permutation, сравненивая соседние элементы как пары двух чисел,
// строим вектор номеров новых классов эквивалентности newClassesNumbers.
newClassesNumbers[permutation[0]] = 0;
classes = 1;
for (int i=1; i<size; ++i)
{
int mid1 = (permutation[i] + (1<<h)) % size;
int mid2 = (permutation[i-1] + (1<<h)) % size;
if (classesNumbers[permutation[i]] != classesNumbers[permutation[i-1]] ||
classesNumbers[mid1] != classesNumbers[mid2])
++classes;
newClassesNumbers[permutation[i]] = classes-1;
}
memcpy(classesNumbers, newClassesNumbers, size * sizeof(int));
}
delete [] newClassesNumbers;
delete [] pn;
delete [] classesNumbers;
}
/**
* Выполняет первый шаг (инициализацию) алгоритма построения суффиксного массива.
* Подсчетом сортирует циклические подстроки длины 1, т.е. отдельные символы,
* и делит их на классы эквивалентности (одинаковые символы относит к одному классу эквивалентности).
* На вход принимает строку in, для которой строится суффиксный массив, незаполненный вектор permutation и
* незаполненный вектор номеров классов эквивалентности classesNumbers, имеющие то же количество элементов,
* что и строка in. Заполняет вектора permutation и classesNumbers.
* Возвращает количество классов эквивалентности.
*/
int suffixArrayBuild_firstStep(const byte* in, int size, int* permutation, int* classesNumbers)
{
countingSortPermutation(in, size, permutation);
// Проходом по вектору перестановки permutation, сравненивая соседние символы,
// строим вектор номеров классов эквивалентности classesNumbers.
classesNumbers[permutation[0]] = 0;
int classes = 1;
for (int i=1; i<size; ++i)
{
if (in[permutation[i]] != in[permutation[i-1]])
++classes;
classesNumbers[permutation[i]] = classes - 1;
}
return classes;
}
/**
* Подсчетом вычисляет такую перестановку permutation символов строки in,
* после которой символы окажутся расставлены в алфавитном порядке. Сортировка может быть нестабильной.
*/
void countingSortPermutation(const byte* in, int size, int* permutation)
{
// сount[] массив подсчета одинаковых элементов при сортировке подсчетом.
// Требуется инициализация 0-го элемента нулем.
int* count = new int[max(size, ALPHABET)];
memset(count, 0, ALPHABET * sizeof(int));
for (int i=0; i<size; ++i)
++count[in[i]];
for (int i=1; i<ALPHABET; ++i)
count[i] += count[i-1];
for (int i=0; i<size; ++i)
permutation[--count[in[i]]] = i;
delete [] count;
}
/**
* Подсчетом вычисляет такую перестановку по первым элементам пар permutation
* перестановки pn по вторым элементам пар, после которой пары окажутся расставлены в алфавитном порядке.
* Сортировка стабильна.
*/
void countingSortPermutationOnSecondElementsOfPairs(const int* pn, int size, int* permutation,
const int* classesNumbers, int classes)
{
// сount[] массив подсчета одинаковых (по классу эквивалентности classesNumbers)
// элементов при сортировке подсчетом. Требуется инициализация 0-го элемента нулем.
int* count = new int[max(size, ALPHABET)];
memset(count, 0, classes * sizeof(int));
// missKiller[] временный массив. Введен для порьбы с кеш промахами.
int* missKiller = new int[size];
/*
* for (int i=0; i<size; ++i)
* ++count[classesNumbers[pn[i]]];
* Этот код эквивалентен следующему.
* Он заменен для уменьшения количества кэш промахов.
*/
for (int i=0; i<size; ++i)
missKiller[i] = classesNumbers[pn[i]];
for (int i=0; i<size; ++i)
++count[missKiller[i]];
// Конец эквивалентного кода.
for (int i=1; i<classes; ++i)
count[i] += count[i-1];
/*
* for (int i=size-1; i>=0; --i)
* permutation[--count[classesNumbers[pn[i]]]] = pn[i];
* Этот код эквивалентен следующему.
* Он заменен для уменьшения количества кэш промахов.
*/
for (int i=size-1; i>=0; --i)
missKiller[i] = classesNumbers[pn[i]];
for (int i=size-1; i>=0; --i)
missKiller[i] = --count[missKiller[i]];
for (int i=size-1; i>=0; --i)
permutation[missKiller[i]] = pn[i];
// Конец эквивалентного кода.
delete [] missKiller;
delete [] count;
}