KMP prefix table



我正在阅读有关字符串匹配KMP的信息。
它需要通过构建前缀表来预处理模式。
例如,对于字符串ababaca前缀表为:P = [0, 0, 1, 2, 3, 0, 1]
但我不清楚这些数字显示了什么。我读到它有助于在模式移动时找到模式的匹配项,但我无法将此信息与表中的数字联系起来。

每个数字都属于相应的前缀("a","ab","aba",...),对于每个前缀,它表示此字符串中与前缀匹配的最长后缀的长度。我们在这里不把整个字符串算作后缀或前缀,它被称为自后缀和自前缀(至少在俄语中,不确定英语术语)。

所以我们有字符串"ababaca"。让我们来看看它。KMP 为每个非空前缀计算前缀函数。让我们将s[i]定义为字符串,p[i]定义为前缀函数。前缀和后缀可能重叠。

+---+----------+-------+------------------------+
| i |  s[0:i]  | p[i]  | Matching Prefix/Suffix |
+---+----------+-------+------------------------+
| 0 | a        |     0 |                        |
| 1 | ab       |     0 |                        |
| 2 | aba      |     1 | a                      |
| 3 | abab     |     2 | ab                     |
| 4 | ababa    |     3 | aba                    |
| 5 | ababac   |     0 |                        |
| 6 | ababaca  |     1 | a                      |
|   |          |       |                        |
+---+----------+-------+------------------------+

计算字符串 S 的前缀函数的简单C++代码:

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;
}

这段代码可能不是最短的,但易于理解的数据流。用于计算前缀的简单Java代码-数组-

    String pattern = "ababaca";
    int i = 1, j = 0;
    int[] prefixArray = new int[pattern.length];
    while (i < pattern.length) {
        while (pattern.charAt(i) != pattern.charAt(j) && j > 0) {
            j = prefixArray[j - 1];
        }
        if (pattern.charAt(i) == pattern.charAt(j)) {
            prefixArray[i] = j + 1;
            i++;
            j++;
        } else {
            prefixArray[i] = j;
            i++;
        }
    }
    for (int k = 0; k < prefixArray.length; ++k) {
        System.out.println(prefixArray[k]);
    }

它产生所需的输出-

0012301

Python 实现

p='ababaca'
l1 = len(p)
j = 0
i = 1
prefix = [0]
while len(prefix) < l1:
    if p[j] == p[i]:
        prefix.append(j+1)
        i += 1
        j += 1
    else:
        if j == 0:
            prefix.append(0)
            i += 1
        if j != 0:
            j = prefix[j-1]
print prefix

我已经尝试过使用Javascript,开放建议。

const prefixArray = function (p) {
let aux = Array(p.length).fill(0);
// For index 0 the matched index will always be 0, so we will we start from 1
let i = 1;
let m = 0; // mismatched index will be from 0th
// run the loop on pattern length
while ( i < p.length) {
    // 3 Cases here
    // First when we have a match of prefix and suffix of pattern
    if(p.charAt(i) === p.charAt(m)) {
        // increment m
        m++;
        // update aux index
        aux[i] = m;
        // update the index.
        i++;
    } 
    // Now if there is no match and m !=0 means some match happened previously
    // then we need to move back M to that index
    else if(p.charAt(i) !== p.charAt(m) && m !== 0) {
        m = aux[m-1];
        // we dont want to increment I as we want to start comparing this suffix with previous matched
    } else {
        // if none of the above conditions then
        // just update the current index in aux array to 0
        aux[i] = 0; // no match
        i++; // shift to the next char
    }
}
return aux; 
}
<</div> div class="one_answers">

无偏移版本

这是基于我所谓的待办事项索引的想法:

int confix[1000000];
void build_confix(char *pattern) {
    // build len %
    int len_pat = strlen(pattern);
   
    // i, j using todo-indexing.
    int j, i;
    confix[j = 1] = i = 0;
    while (j < strlen(pattern)) {
        whlie (i && pattern[j] != pattern[i])
            // length -> length mapping, no offset
            i = confix[i];
        confix[++j] = pattern[j] == pattern[i]?
            ++i:
              0;
    }
}

然后你可以使用这个confix[]表在中间找到needle s(test

int kmp_find_first(char *test, char *needle) {
    int j = 0, i = 0;
    while (j < strlen(test)) {
        while (i && test[j] != needle[i])
            i = confix[i];
        ++j; test[j] == needle[i]?
            ++i:
              0;
        if (i == strlen(needle))
            return j - strlen(needle);
    }
    return -1;
}

最新更新