我所理解的 KMP(Knuth–Morris–Pratt) 算法
2015-12-25 23:39
302 查看
假设要在 haystack 中匹配 needle .
要理解 KMP 先需要理解两个概念 proper prefix 和 proper suffix,由于找到没有合适的翻译,暂时分别称真实前缀 和 真实后缀。
真实前缀(Proper prefix): 一个字符串中至少不包含一个尾部字符的前缀字符串。例如 "Snape" 的全部真实前缀是 “S”, “Sn”, “Sna”, and “Snap” .
真实后缀(Proper suffix): 一个字符串中至少不包含一个头部字符的后缀字符串。例如 “Hagrid” 的全部真实后缀是 “agrid”, “grid”, “rid”, “id”, and “d”.
KMP 的一个基本思想是:无论何时遇到匹配失败,我们已经匹配了一部分 needle 的字符串,它是 needle 的一个真实前缀,利用这个已匹配的真实前缀,我们可以避免重复匹配。如果想理解这个基本思想,可以参照 维基百科的例子 Knuth–Morris–Pratt algorithm example。
了解上面的基本思想后, 如何有效地利用 needle 的真实前缀信息?这个需要用一个数组 LofPS 记录。
LofPS[i] 表示 needle[0...i] 中既是真实前缀又是真实后缀的最长字符串长度。
上面这句话有点绕,但是仍然需要理解,因为无论少了那部分短语,意思都会不完整。
参考资料:
Searching for Patterns | Set 2 (KMP Algorithm), geeksforgeeks
The Knuth-Morris-Pratt Algorithm in my own words, jBoxer
Worked example in Knuth–Morris–Pratt algorithm, wikipedia
要理解 KMP 先需要理解两个概念 proper prefix 和 proper suffix,由于找到没有合适的翻译,暂时分别称真实前缀 和 真实后缀。
真实前缀(Proper prefix): 一个字符串中至少不包含一个尾部字符的前缀字符串。例如 "Snape" 的全部真实前缀是 “S”, “Sn”, “Sna”, and “Snap” .
真实后缀(Proper suffix): 一个字符串中至少不包含一个头部字符的后缀字符串。例如 “Hagrid” 的全部真实后缀是 “agrid”, “grid”, “rid”, “id”, and “d”.
KMP 的一个基本思想是:无论何时遇到匹配失败,我们已经匹配了一部分 needle 的字符串,它是 needle 的一个真实前缀,利用这个已匹配的真实前缀,我们可以避免重复匹配。如果想理解这个基本思想,可以参照 维基百科的例子 Knuth–Morris–Pratt algorithm example。
了解上面的基本思想后, 如何有效地利用 needle 的真实前缀信息?这个需要用一个数组 LofPS 记录。
LofPS[i] 表示 needle[0...i] 中既是真实前缀又是真实后缀的最长字符串长度。
上面这句话有点绕,但是仍然需要理解,因为无论少了那部分短语,意思都会不完整。
vector<int> LofPS; /** * 求 s 中所有前缀字符串[0...i]各自的 既是真实前缀又是真实后缀的子字符串最长长度,存于 LofPS[i]。 * * 例如令 len = LofPS[i],则表示 真实前缀s[0...len-1] 和 真实后缀s[ i-len+1...i ] 相等。 * */ vector<int> computePrefixSuffix(string s){ // LofPS[i] 表示 s[0....i] 部分中,既是真实前缀又是真实后缀的子字符串最长长度。 vector<int> LofPS(s.size()); if (s.size() == 0) { return LofPS; } LofPS[0] = 0; int len = 0; int i = 1; while (i < s.size()) { if (s[i] == s[len]) { len++; LofPS[i] = len; i++; continue; } if (len != 0) { // 利用之前计算的结果。这里是一个需要理解的点。 // 根据已计算的 LofPS[len-1]部分 真实前缀、真实后缀的相等的最长长度,定位同样匹配 s 前缀但是更短的子字符串。 len = LofPS[len - 1]; }else{ LofPS[i] = 0; i++; } } return LofPS; } int KMPsearch(string haystack, string needle) { // 计算 needle 中所有前缀字符串[0...idx]各自的真实前缀且是真实后缀的最长长度。 vector<int> tmp(needle.size()); LofPS = tmp; LofPS = computePrefixSuffix(needle); int i = 0 ; int k = 0; while (i < haystack.size() && k < needle.size()) { if (haystack[i] == needle[k]) { i++; k++; continue; } if (LofPS[k-1] != 0) { k = LofPS[k-1]; continue; } if (haystack[i] == needle[0]) { k = 1; i++; }else{ k = 0; i++; } } if (k == needle.size()) { return i - k; }else{ return -1; } }
参考资料:
Searching for Patterns | Set 2 (KMP Algorithm), geeksforgeeks
The Knuth-Morris-Pratt Algorithm in my own words, jBoxer
Worked example in Knuth–Morris–Pratt algorithm, wikipedia
相关文章推荐
- Linux文本处理三剑客之sed
- EF FluentAPI映射一对多 关系时候报错
- 灌输一些观念
- 对象序列化为何要定义serialVersionUID的来龙去脉
- asp.net MVC4的执行流程
- LeetCode Find Minimum in Rotated Sorted Array
- 存储器的分配与回收算法实现
- Angular-依赖注入
- javaMap
- Objective-C中的浅拷贝和深拷贝
- SQL中视图整理
- SQLServer 维护脚本分享(05)内存(Memory)
- 使用加密解密技术和CA认证解决网络传输中的安全隐患
- boost库实现Base64编解码
- LayoutInflater
- 工作六年六个月
- SQL Server汇总数据之聚合函数与分组
- 简单研究下APK文件
- Hdu 2050解题报告
- 嵌入式C笔试小结