模式匹配算法KMP
来源:互联网 发布:2017nba球员数据排名 编辑:程序博客网 时间:2024/06/05 18:41
KMP算法是一种高效的前缀匹配算法,在传统蛮力(BF)匹配算法的基础上改进的地方在于每次移动的距离不是1可以是更大,没有进行回溯,BF算法的时间复杂度是O(m*n),而KMP算法的时间复杂度是O(m+n)。
T[i,i+1,...,i+j-1]=P[0,1,...,j-1],T[i+j]≠P[j] (打不了下标,就有数组的形式给出字符串) (1)
BF算法下一趟是从目标的第i+1位置开始与模式串比较。如果匹配成功则有
T[i+1,i+2,...,i+m]=P[0,1,...m-1] (2)
如果模式串P有如下特征
P[0,1,...j-2]=P[1,2,...j-1] (3)
由(1)可知
T[i+1,i+2,...,i+j+1]=P[1,2,...j-1] (4)
由(3)(4)可知
T[i+1,i+2,...,i+j+1]≠P[0,1,...j-2] (5)
故由
T[i+1,i+2,....,i+m]≠P[0,1,...m-1]
所以第i+2趟是匹配可以不需要进行,因为一定不能匹配。
类似可以推得
P[0,1,...k-1]=P[j-k-1,j-k,...j-1]
这时才有
P[0,1,...k-1]=P[j-k-1,j-k,...j-1]=T[i+j-k,i+j-k+1,i+j-1]
模式串P从当前位置直接向右移动 j-k 位置,使模式串P的第 k 个字符P[k]与目标串T中的第i+j个字符对齐开始比较(前面 k 个已经匹配)。
造成BF算法效率低的主要原因是在算法执行过程中有回溯,而这些回溯是可以避免的。KMP算法的关键是在匹配失败时,确定下一次匹配的位置,设next[j]=k,表示当模式串P中第j个字符与母串T相应字符不匹配时,模式串P中应当由第K个字符与目标串中刚不匹配的字符对齐继续进行比较。
例如,模式串P="abaabcac",其对应的next[j]如下:
i
0
1
2
3
4
5
6
7
t[i]
a
b
d
a
b
c
d
e
next[i]
-1
0
0
0
1
2
0
0
next数组构造
╔ -1, j=0;
next[j]= ║max{k| 0<k<j 且 P[0,1,...,k-1]=P[j-k,j-k+1,..j-1}
╚ 0, 其他情况
next数组求解是一个递推过程,
设next[j]=k,则有
P[0,1,...k-1]=P[j-k,j-k+1,...,j-1]
next[j]= ╔ max{k| 0<k<j 且 P[0,1,...,k]=P[j-k,j-k+1,..j-1}
╚ 0, 其他情况
如果P[k]=P[j],有 next[j+1]=next[j]+1=k+1。
如果P[k]≠P[j],有 P[0,1,...,k]≠P[j-k,j-k+1,...j],
假设next[j+1]=h+1,则有下式成立
P[0,1,...h]=P[j-h+1,j-k+1,...j] P[h]=P[j]
又因为
P[0,1,...h-1]=P[j-h,j-k+1,...j-1]=P[k-h,k-h+1,k-1] (next[k]=h的情况)
即此时实际只需要满足 next[k]=h(前面已经求解过)时,P[h]=P[j] 就有next[j+1]=h+1,否则(不存在这样的h)next[j+1]等于0。
由此可以得到计算next的递推公式
KMP算法实现
/* ******************************************************************* created: 2006/07/02 filename: KMP.cpp author: 李创 http://www.cppblog.com/converse/ 参考资料: 严蔚敏<<数据结构>> purpose: KMP字符串匹配算法的演示******************************************************************** */ #include < stdio.h > #include < stdlib.h > #include < assert.h > #include < string .h > #define MAX_LEN_OF_STR 30 // 字符串的最大长度 typedef struct String // 这里需要的字符串数组,存放字符串及其长度 { char str[MAX_LEN_OF_STR]; // 字符数组 int length; // 字符串的实际长度 } String, * PString; // 得到字符串的next数组 void GetNextArray(PString pstr, int next[]) { assert(NULL != pstr); assert(NULL != next); assert(pstr -> length > 0 ); // 第一个字符的next值是-1,因为C中的数组是从0开始的 next[ 0 ] = - 1 ; for ( int i = 0 , j = - 1 ; i < pstr -> length - 1 ; ) { // i是主串的游标,j是模式串的游标 // 这里的主串和模式串都是同一个字符串 if ( - 1 == j || // 如果模式串游标已经回退到第一个字符 pstr -> str[i] == pstr -> str[j]) // 如果匹配成功 { // 两个游标都向前走一步 ++ i; ++ j; // 存放当前的next值为此时模式串的游标值 next[i] = j; } else // 匹配不成功j就回退到上一个next值 { j = next[j]; } } } // KMP字符串模式匹配算法 // 输入: S是主串,T是模式串,pos是S中的起始位置 // 输出: 如果匹配成功返回起始位置,否则返回-1 int KMP(PString S, PString T, int pos) { assert(NULL != S); assert(NULL != T); assert(pos >= 0 ); assert(pos < S -> length); if (S -> length < T -> length) return - 1 ; printf( " 主串\t = %s\n " , S -> str); printf( " 模式串\t = %s\n " , T -> str); int * next = ( int * )malloc(T -> length * sizeof ( int )); // 得到模式串的next数组 GetNextArray(T, next); int i, j; for (i = pos, j = 0 ; i < S -> length && j < T -> length; ) { // i是主串游标,j是模式串游标 if ( - 1 == j || // 模式串游标已经回退到第一个位置 S -> str[i] == T -> str[j]) // 当前字符匹配成功 { // 满足以上两种情况时两个游标都要向前进一步 ++ i; ++ j; } else // 匹配不成功,模式串游标回退到当前字符的next值 { j = next[j]; } } free(next); if (j >= T -> length) { // 匹配成功 return i - T -> length; } else { // 匹配不成功 return - 1 ; } }
- 模式匹配---KMP算法
- 模式匹配 KMP算法
- 模式匹配-KMP算法
- KMP模式匹配算法
- KMP模式匹配算法
- KMP模式匹配算法
- 模式匹配kmp算法
- 模式匹配算法kmp
- KMP模式匹配算法
- KMP模式匹配算法
- KMP模式匹配算法
- KMP模式匹配算法
- KMP模式匹配算法
- KMP模式匹配算法
- KMP模式匹配算法
- KMP模式匹配算法
- KMP算法模式匹配
- KMP 模式匹配算法
- 百度直达号入口
- Java Serializable(序列化)的理解和总结
- WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform… using builtin-ja
- 360影响android studio正常运行
- MySQL中同时存在创建和上次更新时间戳字段解决方法浅析
- 模式匹配算法KMP
- java 基础之transient
- 和后台交互
- 常用的Linux指令+Android下的Linux指令
- 大话Android Touch事件传递机制
- C#使用Log4Net记录日志
- Struts2 全局结果集
- Eclipse导入Tomcat源码
- 使用Spring的MailSender发送邮件