LCP如何帮助查找模式的出现次数?

时间:2012-07-07 08:18:12

标签: java algorithm data-structures pattern-matching suffix-array

我已经读过最长公共前缀(LCP)可用于查找字符串中模式的出现次数。

具体来说,您只需要创建文本的后缀数组,对其进行排序,然后不进行二进制搜索以找到范围,以便您可以计算出现次数,只需计算每个连续的LCP即可。在后缀数组中输入。

虽然使用二进制搜索来查找模式的出现次数很明显,但我无法弄清楚LCP如何帮助找到这里出现的次数。

例如banana的后缀数组:

LCP  Suffix entry
N/A  a  
1    ana  
3    anana  
0    banana  
0    na  
2    nana  

LCP如何帮助找到像“banana”或“na”这样的子字符串的出现次数对我来说并不明显。

有任何帮助可以找出LCP如何帮助吗?

2 个答案:

答案 0 :(得分:31)

我不知道使用LCP数组代替执行二进制搜索的方法,但我相信你所指的是Udi Manber和Gene Myers在{{3 }}

这个想法是这样的:为了找到文本T(长度N)中给定字符串P(长度为m)的出现次数,

  • 您对T的后缀数组使用二进制搜索(就像您建议的那样)
  • 但是加速使用LCP阵列作为辅助数据结构。更具体地说,您生成一个特殊版本的LCP阵列(我将其称为LCP-LR)并使用它。

使用标准二进制搜索(没有 LCP信息)的问题是,在你需要进行的每个O(log N)比较中,你比较P到后缀数组的当前条目,这意味着最多m个字符的完整字符串比较。所以复杂度是O(m * log N)。

LCP-LR阵列通过以下方式帮助将其提高到O(m + log N):

  • 在二进制搜索算法期间的任何时候,您都会像往常一样考虑后缀数组的范围(L,...,R)及其中心点M,并决定是否继续在左子搜索中搜索-range(L,...,M)或右子范围(M,...,R)。
  • 为了做出决定,你将P与M处的字符串进行比较。如果P与M相同,则完成,但如果不是,则将比较P的前k个字符,然后判断P是否为按字典顺序小于或大于M.让我们假设结果是P大于M.
  • 因此,在下一步中,您考虑(M,...,R)和新的中心点M'在中间:

                  M ...... M' ...... R
                  |
           we know:
              lcp(P,M)==k
    

    技巧现在是预先计算LCP-LR,使得O(1)-lookup告诉你最长的M和M&,lcp(M,M&#39)的公共前缀;)

    您已经知道(从上一步开始)M本身具有与P:lcp(P,M)= k共同的k个字符的前缀。现在有三种可能性:

    • 案例1:k< lcp(M,M'),即P具有与M共同的更少的前缀字符,而M与M'相同。这意味着M'的第(k + 1)个字符。与M的相同,并且由于P在字典上大于M,因此它必须在字典上大于M'。所以我们继续在右半边(M',...,R)。
    • 案例2:k> lcp(M,M'),即P具有与M共同的更多前缀字符,而M与M'相同。因此,如果我们将P与M'进行比较,则公共前缀将小于k,并且M'在字典上会比P大,所以,没有实际进行比较,我们继续在左半边(M,...,M')。
    • 案例3:k == lcp(M,M')。所以M和M'在前k个字符中都与P相同。为了确定我们是继续在左半部分还是在右半部分,将P与M'进行比较就足够了。 从第(k + 1)个字符开始
  • 我们继续递归。

总体效果是不会将P的任何字符与文本的任何字符进行多次比较。字符比较的总数以m为界,因此总复杂度确实为O(m + log N)。

显然,剩下的关键问题是我们如何预先计算LCP-LR,以便它能够在O(1)时间告诉我们后缀数组的任何两个条目之间的lcp?如上所述,标准LCP阵列仅告诉您连续条目的lcp ,即任何x的lcp(x-1,x)。但M和M'在上面的描述中不一定是连续的条目,那么怎么做呢?

关键是要意识到在二分搜索过程中只会出现某些范围(L,...,R):它始终以(0,...,N)开头并将其除以中心,然后继续向左或向右再划分那一半,依此类推。如果你想到它:后缀数组的每个条目都在二进制搜索期间作为一个可能范围的中心点出现。因此,正好有N个不同的范围(L ... M ... R)可能在二进制搜索中起作用,并且足以为那些N可能预先计算lcp(L,M)和lcp(M,R)范围。因此,这是2 * N个不同的预先计算的值,因此LCP-LR的大小为O(N)。

此外,还有一种直接的递归算法,用于在标准LCP阵列的O(N)时间内计算LCP-LR的2 * N值 - 如果您需要,我建议发布一个单独的问题详细说明。

总结一下:

  • 可以在O(N)时间计算LCP-LR,从LCP计算O(2 * N)= O(N)空间
  • 在二进制搜索期间使用LCP-LR有助于加快搜索过程从O(M * log N)到O(M + log N)
  • 如您所知,您可以使用两个二进制搜索来确定P匹配范围的左端和右端,匹配范围的长度与P的出现次数相对应。

答案 1 :(得分:0)

最长公共前缀(LCP)是后缀树中的最低公共祖先(LCA)。获得最低公共祖先后,您可以计算从LCA分支出来的节点数。这将为您提供后缀树中模式的出现次数。这是LCP和LCA之间的关系。