C++11 玻璃珠-后缀数组在这里是如何应用的?

C++11 玻璃珠-后缀数组在这里是如何应用的?,c++11,lexicographic,suffix-array,C++11,Lexicographic,Suffix Array,此问题的问题说明可在此链接找到-。当我第一次读到这个问题时,我无法想象后缀数组的概念是如何应用于这个问题的。我从这个链接读代码-。如果有人能举一个小例子,并帮助我完整的跟踪应用后缀数组和LCP的概念将是非常有帮助的 此外,我没有在我提到的链接中的代码中理解这一行的含义: 这个作业在做什么-LCP[i+1]==n-1-SA[i] for (int i = 0; i < n; i++) if (SA[i] < (n >> 1)){

此问题的问题说明可在此链接找到-。当我第一次读到这个问题时,我无法想象后缀数组的概念是如何应用于这个问题的。我从这个链接读代码-。如果有人能举一个小例子,并帮助我完整的跟踪应用后缀数组和LCP的概念将是非常有帮助的

此外,我没有在我提到的链接中的代码中理解这一行的含义:

这个作业在做什么-LCP[i+1]==n-1-SA[i]

for (int i = 0; i < n; i++)
            if (SA[i] < (n >> 1)){
                if (i + 1 < n && SA[i + 1] < (n >> 1) && **LCP[i + 1] == n - 1 - SA[i]**) -- 
                    continue;
                printf("%d\n", SA[i] + 1);
                break;
            }
for(int i=0;i>1)){
如果(i+1>1)和&**LCP[i+1]==n-1-SA[i]**)-
继续;
printf(“%d\n”,SA[i]+1);
打破
}

首先让我们了解所使用的概念:

  • 后缀数组:对于由n个字符组成的字符串s,s的后缀数组包含按字典顺序排序的n+1个可能的s后缀。对于紧凑表示,我们可以只为每个后缀存储后缀在s中的起始位置

  • 最长公共前缀数组:为后缀数组中的每两个连续对保留两个后缀中最长公共前缀的大小

对于玻璃珠问题,我们得到一个表示圆形玻璃珠链的输入字符串。我们被要求找到“最薄弱的环节”,即珠子的位置,即在珠子之前切割链条,并考虑从珠子开始绕链条一直到切口的字符串,这是所有可能切割中的最小词典。当存在多个可能的解决方案时,要求我们返回输入字符串中最早出现的切割

请考虑您提供的链接中的示例:

  • helloworld
    :最薄弱的环节在位置10。这意味着我们在
    d
    之前剪切,生成新字符串
    dhelloworl
    。我们可以立即看到没有更好的剪切位置,因为
    d
    是输入字符串中出现的最小字母。因此,没有其他切割可以生成一个新的字符串,该字符串在词典上比以
    d
    开头的字符串小。我们发现,当字符串具有唯一的最小字符时,问题就很简单了,例如本例中的
    d

  • amandamanda
    :最薄弱的环节在位置11。这意味着我们在最后一个
    a
    之前剪切,生成新字符串
    aamandamand
    。同样很明显,没有比切割更好的地方了,因为没有其他切割可以生成以
    aa
    开头的字符串,因此没有其他切割可以生成比
    aamandamand
    小的字符串

  • dontcallmebfu
    :最薄弱的环节在位置6。这意味着我们在
    a
    之前剪切,生成新字符串
    allmebfudontc
    。很明显,这是唯一可能的解决方案,因为
    a
    是输入字符串中唯一的最小字符

  • aaabaaa
    :最薄弱的环节在位置5。这意味着我们在
    b
    后面的
    a
    前面剪切,生成新字符串
    aaaaaa b
    。我们可以看到,这是切割的最佳位置,因为它在
    b
    发生之前生成了最长的
    a
    s序列。因此,这个新字符串在所有可能的切分生成的新字符串中是最小的

现在让我们考虑如何将SA/LCP应用到这个问题:如在第二个链接中所指出的,该方法是为双输入字符串构造SA/LCP。加倍输入字符串意味着连接输入字符串的两个副本。我们为什么要这样做?它允许我们模拟玻璃珠链的圆度。再次考虑示例<代码> HeloRoLLD(大小10)。当我们将输入字符串加倍时,我们得到

helloworldhelloworld
。当我们在输入字符串中的
d
之前剪切时,我们现在可以通过将双字符串中的剪切向前移动10个字符来读取剪切生成的字符串:
helloworl | dhelloworl | d

如果我们仔细观察,我们可以发现,我们实际上永远不需要第二个副本中的最后一个字符。到达第二个
d
的唯一方法是在第一个
d
之后切割。但是我们永远不会在第一个
d
之后进行剪切,因为这相当于在第一个副本的开头进行剪切。因此,作为一个小优化,当创建第二个副本的循环仅上升到
n-1
(其中
n
是输入字符串的长度)时,我们可以省略第二个副本的最后一个字符,这是在第二个链接的代码中完成的:

其中,
n
是原始字符串大小的两倍(在第二个副本中省略最后一个字符的减号将被取消,因为我们将
$
作为终端字符插入)。
>1
是二进制左移一个位置,相当于除以二。因此,该检查确保只考虑双联管柱前半部分的切割

因为问题还有一个附加约束,即当存在多个解决方案时,其中一个会出现 在输入字符串最早应该返回的时候,我们必须对SA条目应用额外的过滤。 从第二个链接中考虑这个例子。我们有输入
AAA
并将其加倍(减1)到
AAAAA
。后缀数组将是:

- 5: `$` (empty word)
- 4: `A$`
- 3: `AA$`
- 2: `AAA$`
- 1: `AAAA$`
- 0: `AAAAA$`
由于上一次检查,5、4、3处的剪切被跳过,因为剪切发生在加倍输入字符串的后半部分。对于其余条目,
2
处的切分将根据
if (SA[i] < (n >> 1)){
- 5: `$` (empty word)
- 4: `A$`
- 3: `AA$`
- 2: `AAA$`
- 1: `AAAA$`
- 0: `AAAAA$`
if (i + 1 < n && SA[i + 1] < (n >> 1) && LCP[i + 1] == n - 1 - SA[i])
  continue;