我读过 最长公共前缀(LCP) 可用于查找字符串中某个模式出现的次数。
具体来说,您只需要创建文本的后缀数组,对其进行排序,然后无需进行二进制搜索来找到范围,从而可以计算出出现的次数,您只需为列表中每个连续的条目计算LCP后缀数组。
尽管使用二进制搜索来查找模式的出现次数很明显,但我无法弄清楚LCP如何在这里帮助找到出现的次数。
例如,此后缀数组用于banana:
banana
LCP Suffix entry N/A a 1 ana 3 anana 0 banana 0 na 2 nana
LCP如何帮助找到像“香蕉”或“ na”这样的子字符串出现的次数对我来说并不明显。
是否有帮助弄清楚LCP在这里有什么帮助?
我不知道使用LCP数组 而不 执行二进制搜索的任何方式,但是我相信您所指的是Udi Manber和Gene Myers在后缀数组中描述的技术:在线字符串搜索的新方法。
(注意:以下说明已复制到2014年4月9日的Wikipedia文章中,请参见diff。如果您在此处和Wikipedia上查看修订历史,则会发现此处的内容是第一个撰写的。请不要插入诸如“来自Wikipedia的评论”之类的评论。)
这个想法是这样的:为了找到在文本T(长度N)中给定字符串P(长度m)的出现次数,
使用标准二进制搜索( 不 包含LCP信息)的问题是,在您需要进行的 每个O(log N)比较中 ,您都将P与后缀数组的当前条目进行比较,这意味着up 的 完整字符串比较 至m个字符。因此复杂度为O(m * log N)。
LCP-LR阵列通过以下方式帮助将其提高到O(m + log N):
M ...... M' ...... R | we know: lcp(P,M)==k
现在的 诀窍 是,对LCP-LR进行了预先计算,以使O(1)查找可以告诉您M和M’的最长公共前缀lcp(M,M’)。
您已经知道(从上一步开始)M本身与P共同具有k个字符的前缀:lcp(P,M)= 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')。因此,在前k个字符中,M和M'与P相同。为了确定我们继续左半还是右半,只要 **从第(k + 1)个字符开始** 比较P和M'就足够了。
总体效果是, 没有将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值–如果您需要对此进行详细说明,我建议您提出一个单独的问题。
总结一下: