如何使用后缀数组和 LCP 数组查找字符串的子字符串?

Posted

技术标签:

【中文标题】如何使用后缀数组和 LCP 数组查找字符串的子字符串?【英文标题】:How to find ith substring of a string using suffix array and LCP array? 【发布时间】:2016-06-12 14:29:00 【问题描述】:

如果我们按字典顺序排列字符串的所有不同子字符串,我们需要第 i 个子字符串

1.) 使用suffix array 和LCP array 是否可以找到它?

2.) 如果是,我们该怎么做?是否可以在使用时间复杂度为 O(Nlog^2N) 的 Manber & Myers 创建后缀数组时在 O(Nlog^N) 中完成,或者在使用时间复杂度为 O(N) 的 Kasai 算法创建 LCP 数组时完成)?

【问题讨论】:

【参考方案1】:

是的,可以使用 Suffix 数组和 LCP 数组来完成。

假设你知道如何计算 Suffix 数组和 LCP 数组。

p[]表示后缀数组lcp[]表示LCP数组。

创建一个数组,该数组存储不同子字符串的数量,直到i'th 等级后缀。这可以使用这个公式来计算。更多详情见Here

cum[]表示累积数组,计算如下:

cum[0] = n - p[0];
for i = 1 to n do:
    cum[i] = cum[i-1] + (n - p[i] - lcp[i])

现在要查找i'th 子字符串,只需在累积数组cum[] 中找到i 的下限,这将为您提供子字符串应开始的后缀排名并打印所有字符直到长度为

i - cum[pos-1] + lcp[pos] // i lies between cum[pos-1] and cum[pos] so for finding 
                          // length of sub string starting from cum[pos-1] we should 
                          // subtract cum[pos-1] from i and add lcp[pos] as it is 
                          // common string between current rank suffix and 
                          // previous rank suffix. 

其中pos 是按下限返回的值。

以上过程可以概括如下:

string ithSubstring(int i)
    pos = lower_bound(cum , cum + n , i);
    return S.substr(arr[pos] , i - cum[pos-1] + lcp[pos]);// considering S as original character string 

关于 Suffix 数组、LCP 及以上逻辑的完整实现,您可以查看Here

【讨论】:

感谢您这么快的回复,我已经想了好几天了。我会尽快理解并实施这个并接受这个作为答案。 :) 我已经添加了上述逻辑的完整实现的链接,如果您在理解上遇到任何问题,您可以检查一下。 还没有,我最近刚刚学习了 O(N) 和 O(log^N) 算法来创建后缀数组,因为我是这个主题的新手,所以需要时间来掌握所有实现 谢谢!我现在明白了 @sudoer 如何从 lcp 打印所有子字符串?

以上是关于如何使用后缀数组和 LCP 数组查找字符串的子字符串?的主要内容,如果未能解决你的问题,请参考以下文章

[TJOI2017]DNA——后缀数组求LCP

后缀数组 hash求LCP BZOJ 4310: 跳蚤

Boring counting HDU - 3518 (后缀数组)

BZOJ4556字符串(后缀数组,主席树)

kuangbin后缀数组 - I题 POJ3415 单调栈解法

bzoj4556: [Tjoi2016&Heoi2016]字符串 (后缀数组加主席树)