【问题标题】:KMP prefix tableKMP 前缀表
【发布时间】:2012-11-27 08:16:50
【问题描述】:

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

【问题讨论】:

标签: string algorithm data-structures pattern-matching


【解决方案1】:

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

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

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

【讨论】:

  • 更新了我的答案,希望现在更好。如果您在使用 KMP 时仍然遇到问题,您可以选择其他适合您需求的算法:Z-Function 或 Rabin-Karp(带散列)。
  • 你刚刚帮助我完全理解了这一点,我现在可以编写自己的版本了。希望你会喜欢我的赏金!
  • 这里j是什么意思,我知道是p[i],“前缀表”的一个元素,s[j]怎么理解,和s[i]比较
【解决方案2】:

此代码可能不是最短的,但易于理解的代码流。 计算prefix-Array-的简单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]);
    }

它产生所需的输出-

0 0 1 2 3 0 1

【讨论】:

    【解决方案3】:

    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
    

    【讨论】:

      【解决方案4】:

      无偏移版本

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

      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[]表在中间找到needles(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;
      }
      

      【讨论】:

        【解决方案5】:

        字符串文本 = "ababbabbababbabbabbabb"; 静态 int arr[30];

        int i = 1;
        while (i < text.length())
        {
            int j = 0;
            int value = 0;
            while (((i + j) < text.length()) && (text[j] == text[i + j]))
                val[i + j] = ++value, j++;
            i += j + 1;
        }
        

        需要的输出存储在 val[]

        【讨论】:

          【解决方案6】:

          我已经尝试过使用 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; 
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-08-02
            • 1970-01-01
            • 1970-01-01
            • 2014-05-02
            • 2018-10-18
            • 1970-01-01
            • 2014-01-30
            相关资源
            最近更新 更多