我编写了一个Suffix Array实现,并在我的实现中发现了一个问题。具体而言,我输出了此string(长度= 10 ^ 5)的前几个后缀数组等级RA[0..7]
,并具有以下输出:
80994
84360
87854
91517
95320
99277
83068
但正确的必须是(一切都移动了23):
81017
84383
87877
91540
95343
99300
83091
我知道如何修复它的两种方法,但我不知道它为什么会起作用。
第一种方法是将S[N++] = '$';
添加到buildSA()
函数的顶部(然后输出比正确值小1,但这并不重要)
我还通过将MAX_N常数减小到1e5 + 10来找到另一种解决方案!
这对我来说太神奇了,我真的需要知道为什么会发生这个错误,因为我不想再犯这个错误。
#include <cstdio>
#include <cstring>
#include <algorithm>
using std::max;
const int MAX_N = 2e5 + 10;
int SA[MAX_N]; // The ith element is the index of the suffix
int RA[MAX_N]; // The rank of the suffix at i
int tmp[MAX_N]; // A temporary array
int B[MAX_N]; // An array for the buckets
int N;
char S[MAX_N];
void bucketSort(int k){
int i, m = max(256, N);
for(i = 0; i < m; i++)
B[i] = 0;
for(i = 0; i < N; i++)
B[i + k < N ? RA[i + k] : 0] ++;
for(i = 1; i < m; i++)
B[i] += B[i - 1];
for(i = N - 1; i >= 0; i--)
tmp[--B[SA[i] + k < N ? RA[SA[i] + k] : 0]] = SA[i];
for(i = 0; i < N; i++)
SA[i] = tmp[i];
}
void buildSA(){
for(int i = 0; i < N; i++){
SA[i] = i;
RA[i] = S[i];
}
for(int k = 1; k < N; k <<= 1){
bucketSort(k);
bucketSort(0);
int norder = 0;
tmp[SA[0]] = 0;
for(int i = 1; i < N; i++){
if(RA[SA[i]] == RA[SA[i - 1]] && RA[SA[i] + k] == RA[SA[i - 1] + k])
{} else norder++;
tmp[SA[i]] = norder;
}
for(int i = 0; i < N; i++)
RA[i] = tmp[i];
if(norder == N)
break;
}
}
void printSA(){
for(int i = 0; i < N; i++){
printf("%d: %s\n", SA[i], S + SA[i]);
}
}
int main(){
scanf("%s", S);
N = strlen(S);
buildSA();
for(int i = 0; i < 7; i++){
printf("%d\n",RA[i]);
}
return 0;
}
答案 0 :(得分:1)
在以下行中:
if(RA[SA[i]] == RA[SA[i - 1]] && RA[SA[i] + k] == RA[SA[i - 1] + k])
SA[i] + k
可以是&gt; = N
(同样适用于SA[i - 1] + k
)
它应该是(SA[i] + k) % N
而是。
答案 1 :(得分:0)
我想我经过多次浪费时间才得到它。有时,最小的错误可能导致错误的答案。
“坏”代码行是:
if(RA[SA[i]] == RA[SA[i - 1]] && RA[SA[i] + k] == RA[SA[i - 1] + k])
{} else norder++;
我通过使用一个非常简单的测试用例(我无法随机生成......)来验证这一点,例如:
abab
生成的后缀数组是
0: abab
2: ab
3: b
1: bab
这显然是错误的。
在步骤k = 2时,如果我们比较ab
和abab
之类的两个后缀,那么我们就会发现它们具有相同的等级,因为它们的第一个k = 2个字符匹配。 ab
是后缀#2,通过添加k = 2,我们超出范围。
我经常将它编码为这样,因为我总是在最后添加一个辅助字符(例如'$')。如果我没有放这样的字符(就像在我的情况下),SA [i] + k实际上可能是&gt; = N并且此代码崩溃。