C++ 使用后缀数组和LCP(-LR)实现字符串模式匹配

C++ 使用后缀数组和LCP(-LR)实现字符串模式匹配,c++,c,string,pattern-matching,C++,C,String,Pattern Matching,在过去的几周里,我试图找出如何有效地在另一个字符串中找到一个字符串模式 我发现在很长一段时间里,最有效的方法应该是使用后缀树。然而,由于这种数据结构在空间上非常昂贵,我进一步研究了后缀数组的使用(使用的空间要少得多)。不同的论文,如“后缀数组:在线字符串搜索的新方法”(Manber&Myers,1993)指出,通过使用二进制搜索和后缀数组以及LCP数组,可以在O(P+log(N))(其中P是模式的长度,N是字符串的长度)中实现对子字符串的搜索 为了理解搜索算法,我特别研究了后一篇论文。在帮助我理

在过去的几周里,我试图找出如何有效地在另一个字符串中找到一个字符串模式

我发现在很长一段时间里,最有效的方法应该是使用后缀树。然而,由于这种数据结构在空间上非常昂贵,我进一步研究了后缀数组的使用(使用的空间要少得多)。不同的论文,如“后缀数组:在线字符串搜索的新方法”(Manber&Myers,1993)指出,通过使用二进制搜索和后缀数组以及LCP数组,可以在O(P+log(N))(其中P是模式的长度,N是字符串的长度)中实现对子字符串的搜索

为了理解搜索算法,我特别研究了后一篇论文。在帮助我理解算法方面做了大量的工作(顺便说一句,它被应用到了应用程序中)

但我仍在寻找实现该算法的方法。特别是上述LCP-LR阵列的构造似乎非常复杂

参考资料:

曼伯和迈尔斯,1993年:曼伯大学;迈尔斯,基因,暹罗计算杂志,1993年,第22卷(5),第935-948页

更新1

我只想强调一下我感兴趣的内容:我了解LCP阵列,并找到了实现它们的方法。然而,“普通”LCP阵列不适用于有效的模式匹配(如参考文献中所述)。因此,我对实现LCP-LR阵列感兴趣,这似乎比仅仅实现LCP阵列要复杂得多

更新2

添加到参考文件的链接是一篇不错的文章,其中包含一些代码,可以帮助您更好地理解LCP阵列和比较实现

我理解你的愿望是代码,而不是实现你自己的。 虽然Sedgewick和Wayne从他们的。它应该可以为您节省一些时间,而且移植到C/C++也不会非常困难


为那些可能想要更多关于算法的信息。

< p>这里是一个相当简单的C++实现,虽然 Bug()//Cuff>程序在<代码> O(n LG^ 2 n)时间中建立后缀数组。
lcp\u compute()
过程具有线性复杂性。我在许多编程竞赛中使用过这段代码,它从未让我失望:)


可以帮助您的术语:
增强后缀数组
,用于用各种其他数组描述后缀数组,以替换后缀树(,子)

以下是一些示例:

ESAXX

MKESA

esaxx one似乎正在做您想要做的事情,另外,它还提供了如何使用它的示例enumSubstring.cpp


如果您查看引用的,它会提到一个有用的属性
(4.2)
。因为SO不支持数学,所以在这里复制它是没有意义的

我已经完成了快速实现,它使用段树:

// note that arrSize is O(n)
// int arrSize = 2 * 2 ^ (log(N) + 1) + 1; // start from 1

// LCP = new int[N];
// fill the LCP...
// LCP_LR = new int[arrSize];
// memset(LCP_LR, maxValueOfInteger, arrSize);
// 

// init: buildLCP_LR(1, 1, N);
// LCP_LR[1] == [1..N]
// LCP_LR[2] == [1..N/2]
// LCP_LR[3] == [N/2+1 .. N]

// rangeI = LCP_LR[i]
//   rangeILeft  = LCP_LR[2 * i]
//   rangeIRight = LCP_LR[2 * i + 1]
// ..etc
void buildLCP_LR(int index, int low, int high)
{
    if(low == high)
    {
        LCP_LR[index] = LCP[low];
        return;
    }

    int mid = (low + high) / 2;

    buildLCP_LR(2*index, low, mid);
    buildLCP_LR(2*index+1, mid + 1, high);

    LCP_LR[index] = min(LCP_LR[2*index], LCP_LR[2*index + 1]);
}

我认为@Erti Chris Eelmaa的算法是错误的

L ... 'M ... M ... M' ... R
       |-----|-----|
左子区域和右子区域应所有包含M。因此,我们无法对LCP-LR阵列进行正常的段树划分。 代码应该是

def lcp_from_i_j(i, j): # means [i, j] not [i, j)
    if (j-i<1) return lcp_2_elem(i, j)
    return lcp_merge(lcp_from_i_j(i, (i+j)/2), lcp_from_i_j((i+j)/2, j)
def lcp_from_i_j(i,j):#表示[i,j]不是[i,j]

如果(j-I)你需要哪种语言?用C/C++实现就可以了。或者任何可以移植到C/C++的语言实现都比用我自己的语言实现要省力。我已经稍微修改了这个问题,因为对实现和库的请求通常都是离题的(尽管考虑到你显然做了一些很好的研究,读者可能会更宽容一些)@ Paddre Ac实现与C++的有很大的不同,所以请选择一种语言来获得更好的帮助。因为我的程序的其余部分是用C++编写的,C++实现它的方法是这样做的。但是,由于我对高效率感兴趣,C实现也会很好。(即使我避免混淆C和C++代码,为了效率,我会这样做;-)我已经有了一个高效的构造实现。但是,正如在这个问题中所说的,我特别感兴趣的是一个可以用于高效模式匹配的实现。据我所知,这仅仅是通过使用“Primes”是不可能的。LCP和数组,因为无法检查所有可能的匹配。如果我在这里出错,请更正;-)@Paddre我将
匹配过程的代码添加到我的回答中,我可以设法测试它。首先:您应该提到,终止符号(通常称为
#
$
)必须由用户添加,并且不是SA构造机制的一部分。一旦我从您的代码中发现这一点很清楚,但这与我之前在许多其他SA/LCP实现中所期望的不同。第二:匹配似乎无法正常工作。我尝试了
“vwvxy”
作为
文本
“vwx”“
as
pattern
(即
concat=“vwvwxy#vwx”)和
match()`返回
false
@Paddre,您不应该添加终止符号。在您提到的情况下
concat
应该是
“vwvwxyvwx”
。尝试一下,您会看到
match()
返回
true
。不使用
str=“vwvxyvwx”
match(6,1)
返回
false
(SA:
{0,6,2,1,7,3,8,4,5}
)不幸的是,它们都没有给我提供有关构造适合高效模式匹配的LCP数组的信息。“增强后缀数组”是一个通用术语正在寻找增强型后缀数组的一个非常具体的方面,这是在OP中引用的论文中描述的LCP-LR数组的构造。具体来说,这使
O(M+logN)
在后缀数组上进行模式匹配。@Burntsushi 5:我的意思不是,这个终端可能会让你更接近于发现
L ... 'M ... M ... M' ... R
       |-----|-----|
def lcp_from_i_j(i, j): # means [i, j] not [i, j)
    if (j-i<1) return lcp_2_elem(i, j)
    return lcp_merge(lcp_from_i_j(i, (i+j)/2), lcp_from_i_j((i+j)/2, j)