使用通配符生成 Knuth–Morris–Pratt 前缀表

Posted

技术标签:

【中文标题】使用通配符生成 Knuth–Morris–Pratt 前缀表【英文标题】:Knuth–Morris–Pratt prefix table generation with wildcard 【发布时间】:2016-02-22 22:40:30 【问题描述】:

我正在实现一个支持通配符的 KMP 字节模式搜索。下面是生成前缀表WITHOUT通配符的算法:

    vector<int> PrefixFunction(string S) 
    
        vector<int> p(S.size());
        int j = 0;
        for (int i = 1; i < (int)S.size(); i++) 
        
            while (j > 0 && S[j] != S[i])
                j = p[j-1];

           if (S[j] == S[i])
                j++;
           p[i] = j;
           
        return p;
   

上述算法的问题在于它不适用于通配符。长话短说,考虑以下数学方程:

    a = bb = ca = c

当不涉及通配符时,这些等式是 100% 正确的。但是,它们不适用于通配符。例如:a = 1b = ??(?? 是通配符)和c = 2a equals bb equals c,但 a does not equal to c

由于通配符的这种怪异特性,上面提到的算法将不起作用!因此,我必须为通配符实现一个特定的算法。 我当前的实现如下所示:

vector<int> GeneratePrefixTable(vector<int> bytes, vector<unsigned char> flags)

    vector<int> prefixTable(bytes.size());
    prefixTable[0] = 0;
    for (int j = 1, m = bytes.size(); j < m; j++)
    
        int largest = 0;
        for (int i = 1; i < j; i++)
        
            bool match = true;
            for (int k = 0; k < i; k++)
            
                if (bytes[k] != bytes[j - i + k + 1] && !flags[k] && !flags[j - i + k + 1])
                
                    //if the bytes do not match and neither of them is a wildcard
                    match = false;
                    break;
                
            
            if (!match)
            
                continue;
            
            largest = i;
        

        prefixTable[j] = largest;
    
    return prefixTable;

变量定义:

    vector&lt;int&gt; bytes 模式。又名针。 vector&lt;unsigned char&gt; flags标志数组,表示某个位置的字节是否是通配符 j 我们正在查看的模式的索引生成前缀# largestOne 迄今为止发现的最大前缀长度 i 我们正在测试的前缀长度

请注意,一旦发现不工作的前缀长度,我并没有停止搜索。这也是由于通配符的奇怪属性。 例如,考虑模式01 02 ?? 02 00

    长度为1:前缀:1后缀:0,不匹配 长度为2:前缀:1 2后缀:2 0不匹配 长度为3:前缀1 2 ??后缀:?? 2 0匹配!

由于这个奇怪的属性,我必须测试每个可能的前缀长度。这进一步减慢了我的算法。有哪些方法可以加快我的前缀表生成算法?

【问题讨论】:

也许这会有所帮助:MoreKMP.pdf 或者这个:KMPWildcardMatch.java 上面链接下的“KMPWildcardMatch.java”不正确。失败的一个例子:s="abczzzdXzde"p = "abc???de" 【参考方案1】:

您混淆了两种类型。 “字符”类型比“搜索模式、字符或通配符”类型少一个元素。

这会导致您错误地认为“a”匹配“??” (通配符)。它没有。模式“a”匹配字符“a”,模式“??”匹配任何字符。明显不同的模式。

考虑正则表达式模式会有所帮助。 [ab][ba] 的模式相同; [0-9][0123456789] 的模式相同,但a 绝对与. 的模式不同。

模式是传递相等的。同样,使用正则表达式样式:[abc] = [acb] = [bca]

KMP 要求您小心区分。给定当前匹配位置和匹配的前缀长度,您需要确定下一个可能的匹配位置。这不涉及模式平等。它涉及模式的交集

Small sidestep:一个模式定义了一组要接受的字符串。两个模式可以说是有交集的,也就是都接受的字符串集合。对于复杂的模式,这很难计算。

实际上,KMP 将找到的搜索前缀与搜索的单词的移位版本进行比较。存储的值是两个模式具有非空交点的最小偏移。

【讨论】:

以上是关于使用通配符生成 Knuth–Morris–Pratt 前缀表的主要内容,如果未能解决你的问题,请参考以下文章

Knuth-Morris-Pratt 算法中的 DFA 构造

php Algoritma Knuth-Morris-Pratt

模板: 字符串模式匹配 Knuth–Morris–Pratt Algorithm

ZOJ 3957: Knuth-Morris-Pratt Algorithm

除了 Knuth-Morris-Pratt、Rabin-Karp 等,还都有哪些可用的字符串匹配算法?

ZOJ 17届校赛 Knuth-Morris-Pratt Algorithm