KMP 前缀表
Posted
技术标签:
【中文标题】KMP 前缀表【英文标题】:KMP prefix table 【发布时间】:2012-11-27 08:16:50 【问题描述】:我正在阅读关于 KMP
的字符串匹配。
它需要通过构建前缀表对模式进行预处理。
例如对于字符串ababaca
,前缀表是:P = [0, 0, 1, 2, 3, 0, 1]
但我不清楚这些数字显示了什么。我读到它有助于在模式变化时找到匹配的模式,但我无法将此信息与表中的数字联系起来。
【问题讨论】:
Prefix Table
在KMP算法中也称为Partial Match Table
。这个博客解释得很漂亮 - The Knuth-Morris-Pratt Algorithm in my own words
【参考方案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[]
表在中间找到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;
【讨论】:
【参考方案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;
【讨论】:
以上是关于KMP 前缀表的主要内容,如果未能解决你的问题,请参考以下文章