String 后缀数组实现错误

String 后缀数组实现错误,string,algorithm,data-structures,suffix-array,String,Algorithm,Data Structures,Suffix Array,我编写了一个后缀数组实现,并在实现中发现了一个问题。具体地说,我已经输出了其中的前几个后缀数组秩RA[0..7](长度=10^5),并有以下输出: 80994 84360 87854 91517 95320 99277 83068 但正确的答案必须是(所有的东西都改变了23): 我知道两种方法来修复它,但我不知道为什么它会起作用 第一种方法是添加S[N++]='$'到buildSA()函数的顶部(然后输出比正确值少1,但这无关紧要) 我还找到了另一个解决方案,将MAX_N常数降低到1e5+10

我编写了一个后缀数组实现,并在实现中发现了一个问题。具体地说,我已经输出了其中的前几个后缀数组秩
RA[0..7]
(长度=10^5),并有以下输出:

80994
84360
87854
91517
95320
99277
83068
但正确的答案必须是(所有的东西都改变了23):

我知道两种方法来修复它,但我不知道为什么它会起作用

第一种方法是添加
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;
}
#包括
#包括
#包括
使用std::max;
常数int MAX_N=2e5+10;
int SA[MAX_N];//第i个元素是后缀的索引
int RA[MAX_N];//后缀在i处的秩
int tmp[MAX_N];//临时阵列
int B[MAX_N];//桶的数组
int N;
字符S[MAX_N];
空箱分拣(整数k){
int i,m=最大值(256,N);
对于(i=0;i=0;i--)
tmp[--B[SA[i]+k对于(int k=1;k
if(RA[SA[i]==RA[SA[i-1]]&&RA[SA[i]+k]==RA[SA[i-1]+k])
SA[i]+k
可以是>=
N
(同样适用于
SA[i-1]+k
)。

它应该是
(SA[i]+k)%N

我想我是在浪费了很多时间后得到的。有时候,最微小的错误都会导致错误的答案

“错误”代码行是:

if(RA[SA[i]] == RA[SA[i - 1]] && RA[SA[i] + k] == RA[SA[i - 1] + k])
      {} else norder++;
我通过使用一个非常简单的测试用例(我不能随机生成…)验证了这一点,比如:

生成的后缀数组为

0: abab
2: ab
3: b
1: bab
这显然是错误的

在步骤k=2,如果我们比较两个后缀,如
ab
abab
,那么我们发现它们具有相同的等级,因为它们的第一个k=2字符匹配。
ab
是后缀#2,通过添加k=2,我们超出了范围


我经常这样编码,因为我总是在结尾附加一个辅助字符(如$)。如果我不加这样的字符(如在我的例子中),SA[I]+k实际上可能>=N,而这段代码会崩溃。

我没有被要求查找bug的问题(一些SOER会这样做),但您必须努力找到触发错误的最小可能输入(寻找触发错误的小输入是您在尝试自行修复错误的过程中首先要做的事情之一)随机测试有助于找到一个小的有问题的输入。我已经做过了,并且运行了测试生成器两个小时……理解错误发生原因的关键是理解你正在编写的算法(即,你不仅仅是在复制别人的代码)。如果你不理解你的算法,并且在你看来它是“神奇的”,然后理解如何修复它将是非常困难的,通常需要进行半随机的更改,直到您发现一些似乎“有效”的东西,至少直到它不起作用,并且只是将其永久化为“神奇”…这是真的。我通过使用一个非常简单的测试用例验证了这一点(我无法随机生成…)例如:abab生成的后缀数组是0:abab 2:ab3:b1:bab,这显然是错误的。
abab
0: abab
2: ab
3: b
1: bab