字符串匹配之KMP算法(POJ 3461 Oulipo)
2014-07-24 19:27
218 查看
几种常见的字符串匹配算法
常见的几种字符串匹配算法包括KMP算法,BM算法,Horspool算法,Sunday算法,fastsearch算法,KR算法等等。这里主要介绍一下KMP算法的匹配法则。KMP算法思想:
KMP算法是由D.E.Knuth与V.R.Pratt和J.H.Morris同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。作为经典的前缀匹配算法,我们来探索一下它的神秘之处。
首先,简单粗暴的字符串匹配算法是这样的:
从左往右,text链(母链)的 i=4 和pat链(子链)的 j=4 匹配失败,遇到障碍了怎么办呢?
pat链整体右移了一步。
在 i=1 和 j=0 处匹配失败,又遇到麻烦了。。。
pat链它又往后移动一步。
这是在 i=4 和 j=2 处匹配失败。。。
你猜它会怎么做?聪明!它又往后移动一步。并把这种匹配模式贯彻到底,直到 i[6..10]时text和pat完全匹配,走了6步。
下面再来看一下KMP算法遇到麻烦是怎么解决的:
KMP遇上麻烦了。。。
KMP一步跳到这了。。。
快看,KMP又跳了!!
它向后走了一步。
快看,AC的色彩!!它匹配成功了!!同样的问题,它只用了4步。
你是怎么做到的呀?简单粗暴以无比崇拜的眼光看着KMP
KMP清了清嗓子,骄傲却谦卑:KMP算法的优点就是字符串匹配失败后的回溯时,利用它**next数组的记忆功能**,避免了再次匹配已经匹配过的字符,从而实现跳跃前进。
next数组是这样来的:
对于模式串 pat = ABABA ,执行这段程序
void get_next() { int i = 0, j = -1; next[0] = -1; while(i < lenp) { if(j == -1 || pat[i] == pat[j]) { i++; j++; next[i] = j; } else j = next[j]; } }
你会得到这样一个序列:
这就是next[]数组了。
怎么样?发现了什么没?看 i = 4时, pat[2..4] == pat[0.. 2 ]。
再换一组:
看 i = 13,pat[7..13] == pat[0..6]。
发现了吧,其实**next数组的取值完全取决于模式串**,与主串无关。并且**next[ j ]的值要取到恰到好处**,以上例来讲,
next[ 13 ] == 6,这样就使得pat[ 0..6 ](从0到next[ 13 ])这7个字符与pat[ 7..13 ](13往前数7个)这7个字符完全一样。这就是恰到好处。
知道了这些,再结合上面的程序演示一些例子,我们会发现,我们肉眼凡胎就可以写出next数组的取值来。
随便给你一个模式串abcabcabcd,能直接写出它的next数组来吗?
说完了next数组,再看看KMP函数体吧!
void KMP() { int i = 0, j = 0; while(i < lent && j < lenp) { if(j == -1 || text[i] == pat[j]) { j++; i++; } else j = next[j]; /*这里用到了next数组*/ } }
通过对主串text 和模式串pat 逐字符的比较,最终得以匹配。
各算法见仁见智的地方就是遇到“麻烦”时的应对策略。
拿刚开始的例子从开始走到结束亲自尝试一下
(建议回到刚开始亲自试一下)
最后,学习KMP算法要知道三句话:next数组起到记忆的作用;next数组的取值仅仅取决于模式串;next[ j ]的值要取到恰到好处。多了解一些别的算法思想对KMP算法的学习也有很大的帮助。
/* POJ 3461 代码附上 */ #include <stdio.h> #include <string.h> char text[1000002], pat[10002]; int next[10002]; int ans, lent, lenp; void get_next() { int i, j; i = 0; j = -1; next[0] = -1; while (i < lenp) { if (j == -1 || pat[i] == pat[j]) ++i, ++j, next[i] = j; else j = next[j]; } } void KMP() { int i = 0, j = 0; get_next(); while (i < lent) { if (j == -1 || text[i] == pat[j]) ++i, ++j; else j = next[j]; if (j >= lenp) { ans++; j=next[j]; //如果不能子链重合 这边要改成j=0; } } } int main() { int t; scanf("%d", &t); while (t--) { ans = 0; scanf("%s", pat);//子链 scanf("%s", text);//母链 lent = strlen(text); lenp = strlen(pat); KMP(); printf("%d\n", ans); } return 0; }
相关文章推荐
- POJ 3461 Oulipo(字符串匹配,KMP算法)
- poj 3461 Oulipo 字符串匹配 KMP算法
- PKU3461(Oulipo)字符串匹配-KMP算法
- POJ-3461 Oulipo-匹配的字符有几个(KMP算法)
- HDU 1686 Oulipo , 同 POJ 3461 Oulipo (字符串匹配,KMP)
- poj 3461 Oulipo kmp字符串匹配
- HDU 1686 Oulipo / POJ 3461 Oulipo / SCU 2652 Oulipo (字符串匹配,KMP)
- POJ 3461 : Qualification - 字符串匹配,KMP算法
- POJ 3461 Oulipo(KMP字符串匹配)
- KMP 字符串匹配 POJ 3461 Oulipo
- POJ 3461 Oulipo (KMP字符串匹配·统计p在s中出现次数)
- POJ 3461 Oulipo KMP算法题解
- POJ-1266/KMP算法/字符串匹配
- POJ 3461 Oulipo[附KMP算法详细流程讲解]
- poj 3461(kmp字符串匹配模板)
- poj-3461 kmp字符串匹配问题
- poj 3461 Oulipo KMP算法
- HDU 1686 Oulipo(KMP算法 字符串匹配)
- POJ 3461 Oulipo (KMP算法)
- POJ 3461 Oulipo (KMP算法)