KMP算法模式匹配
转载请注明出处
http://blog.csdn.net/pony_maggie/article/details/37832707
作者:小马
在一个长串中查找一个子串是较经常使用的操作。各种信息检索系统,文字处理系统都少不了。本文介绍一个很著名的KMP模式匹配算法用于子串查找。
先抛开KMP。正常情况一下我们会怎样设计这个逻辑。一个主串S, 要在里面查找一个子串T,假设找到了返回T在S中開始的位置,否则返回-1。应该不难。须要一个辅助函数,从一个串口的指定位置。取出指定长度的子串。思想是这样:
在主串S中从開始位置。取长度和T相等的子串比罗,若相等,返回该位置的索引值。否则位置添加1, 继续上面的过程。
代码非常easy。例如以下:
//普通方法查找子串
//在主串s中查找t, 若找到匹配,返回索引位置(从0到len-1)
//否则返回-1
int IndexOfString(char* srcString, char* keyString)
{
int nSrcLen = 0;
int nKeyString = 0;
int i = 0;
char szTemp[1024] = {0};//如果足够大 if ((srcString == NULL) || (keyString == NULL))
{
return -1;
} nSrcLen = strlen(srcString);
nKeyString = strlen(keyString);
if (nKeyString > nSrcLen)
{
return -1;
} while(i < (nSrcLen-nKeyString))
{
memset(szTemp, 0x00, sizeof(szTemp));
SubString(srcString, szTemp, i, nKeyString);
if (memcmp(szTemp, keyString, nKeyString) != 0)
{
i++;
}
else return i;
} return -1;
}
再进一步,把辅助函数去掉,通过频繁操作下标也相同能够实现,思想跟上面查不多。仅仅只是换成一个个字符比較。代码例如以下:
int IndexOfString1(char* srcString, char* keyString)
{
int nSrcLen = 0;
int nKeyLen = 0;
int i = 0;
int j = 0;
char szTemp[1024] = {0};//如果足够大 if ((srcString == NULL) || (keyString == NULL))
{
return -1;
} nSrcLen = strlen(srcString);
nKeyLen = strlen(keyString);
if (nKeyLen > nSrcLen)
{
return -1;
} while((i < nSrcLen) && (j <nKeyLen))
{
if (srcString[i] == keyString[j])
{
i++;
j++;
}
else
{
i = i - j + 1;//主串回退到下一个位置
j = 0; //子串又一次開始
}
} if (j >= nKeyLen)
{
return (i - nKeyLen);//找到
} return -1;
}
分析一下上面算法的时间复杂度(两个算法事实上是一样的)。
举个样例,主串是:
“A STRING SEARCHING EXAMPLE CONSISTINGOF
SIMPLE TEXT”
子串是
“STING”
用上面的算法,会发现除了上面标记红色的字符比較了两次。其他都是比較一次,假设主串的长度是m, 子串的长度是n, 时间复杂度基本是O(m+n)。好像不错,效率挺高。再来看一个。主串是:
“000000000000000000000000000000000000000000000000000000000001”
子串是
“00000001”
在脑海里想像一下这个过程,非常easy得出它的时间复杂度是O(m*n)。所以这样的类似穷举的查找子串算法,时间复杂度与主串和子串的内容有非常大关系,复杂度不固定。并且像上面那样的01串在计算机处理中还比較常见,所以须要更好的算法。
KMP(三个发明人的名字首字母)算法能够保证不论哪种情况都能够在O(m+n)的时间复杂度内完毕匹配。它改进的地方是当出现比較不等时,主串中位置不回退,而是利用已经匹配的结果,将子串向右滑动尽可能远的距离后。再继续比較,看个样例:
在第三趟匹配中,当i=12, j=7时。字符不相等,这时不用回退到i=7,j=1又一次比較。而是i不变,j变成next[j]的值即可了。上图中是3, 也就是图中第四趟的比較位置。
这个确实非常强大。如今要解决的问题是next[j]怎样计算。事实上这个推导也不难。非常多算法的书上都有具体的过程,我这里就不赘述了。仅仅把结果给出来:
1 当j = 0时。next[j] =-1。
2 当j =1时。next[j] = 0。
3 满足1 < k < j,且p[0…k-1] =p[j-k…j-1]的最大的k, next[j] = k。
4 其他情况,next[j] = 0。
依据上面的逻辑。非常easy写出一个计算next的函数,例如以下:
static void getNext(char* strSub)
{
int j, k, temp;
int nLen = 0;
j = k = temp = 0;
nLen = strlen(strSub); memset(g_next, 0x00, sizeof(g_next));//每次进来都清空 for (j = 0; j < nLen; j++)
{
if (j == 0)
{
g_next[j] = -1;
}
else if (j == 1)
{
g_next[j] = 0;
}
else //取集合不为空时的最大值
{
temp = j - 1;
for (k = temp; k > 0; k--)
{
if (isEqual(strSub, j, k))
{
g_next[j] = k;
break;
}
}
if (k == 0)//集合为空
{
g_next[j] = 0;
}
}
} }
得到next之后。整个步骤例如以下: 以指针i和j分别表示主串和子串中正在比較的字符。若Si = Pj, 则i和j都增1,继续下一个字符的比較。
否则i不变,j退到next[j]的位置再比較,若相等,再各自增1。否则j再退到next[j]。循环进行。假设中间遇到next[j]为-1的情况。此时主串要增1。子串j变为0,表示要从主串的下一个位置又一次開始比較。
把上面的过程转化为代码:
//KMP算法查找子串
//在主串s中查找t, 若找到匹配,返回索引位置(从0到len-1)
//否则返回-1
int IndexOfStringKMP(char* srcString, char* keyString)
{
int i = 0;
int j = 0;
int nSrcLen = 0;
int nKeyLen = 0; if ((srcString == NULL) || (keyString == NULL))
{
return -1;
} nSrcLen = strlen(srcString);
nKeyLen = strlen(keyString);
if (nKeyLen > nSrcLen)
{
return -1;
} getNext(keyString);//先计算next值 while ((i < nSrcLen) && (j < nKeyLen))
{
if ((srcString[i] == keyString[j]) || (j == -1))
{
i++;
j++;
}
else
{
j = g_next[j];
}
}
if (j >= nKeyLen)
{
return (i - nKeyLen);//找到
} return -1;
}
代码下载地址:
http://download.csdn.net/detail/pony_maggie/7630329
或
https://github.com/pony-maggie/StringIndex
KMP算法模式匹配的更多相关文章
- 字符串模式匹配之KMP算法图解与 next 数组原理和实现方案
之前说到,朴素的匹配,每趟比较,都要回溯主串的指针,费事.则 KMP 就是对朴素匹配的一种改进.正好复习一下. KMP 算法其改进思想在于: 每当一趟匹配过程中出现字符比较不相等时,不需要回溯主串的 ...
- 【模式匹配】KMP算法的来龙去脉
1. 引言 字符串匹配是极为常见的一种模式匹配.简单地说,就是判断主串\(T\)中是否出现该模式串\(P\),即\(P\)为\(T\)的子串.特别地,定义主串为\(T[0 \dots n-1]\),模 ...
- 字符串模式匹配KMP算法
一篇不错的博客:http://www.cnblogs.com/dolphin0520/archive/2011/08/24/2151846.html KMP字符串模式匹配通俗点说就是一种在一个字符串中 ...
- 串的模式匹配和KMP算法
在对字符串的操作中,我们经常要用到子串的查找功能,我们称子串为模式串,模式串在主串中的查找过程我们成为模式匹配,KMP算法就是一个高效的模式匹配算法.KMP算法是蛮力算法的一种改进,下面我们先来介绍蛮 ...
- 利用KMP算法解决串的模式匹配问题(c++) -- 数据结构
题目: 7-1 串的模式匹配 (30 分) 给定一个主串S(长度<=10^6)和一个模式T(长度<=10^5),要求在主串S中找出与模式T相匹配的子串,返回相匹配的子串中的第一个字符在主串 ...
- (原创)数据结构之利用KMP算法解决串的模式匹配问题
给定一个主串S(长度<=10^6)和一个模式T(长度<=10^5),要求在主串S中找出与模式T相匹配的子串,返回相匹配的子串中的第一个字符在主串S中出现的位置. 输入格式: 输入有两行 ...
- 字符串模式匹配——KMP算法
KMP算法匹配字符串 朴素匹配算法 字符串的模式匹配的方法刚开始是朴素匹配算法,也就是经常说的暴力匹配,说白了就是用子串去和父串一个一个匹配,从父串的第一个字符开始匹配,如果匹配到某一个失配了,就 ...
- 串的两种模式匹配方式(BF/KMP算法)
前言 串,又称作字符串,它是由0个或者多个字符所组成的有限序列,串同样可以采用顺序存储和链式存储两种方式进行存储,在主串中查找定位子串问题(模式匹配)是串中最重要的操作之一,而不同的算法实现有着不同的 ...
- 串的模式匹配,KMP算法
串的模式匹配 现考虑一个常用操作,在字符串s(我们称为主串)中的第pos开始处往后查找,看在主串s中有没有和子串p相匹配的的,如果有,则返回字串p第一次出现的位置. 暴力求解 int Index(ch ...
随机推荐
- HDU-1529 Cashier Employment
据网上说这是到差分约束四星题... 可我觉得难吗? 比推DP方程容易... 两种约束方式,当然实现到程序就变成六种了... #include <cstdio> #include <c ...
- BZOJ-1085 骑士精神
估价函数其实就是与目标状态有几个不同... 迭代启发搜索. #include <cstdlib> #include <cstdio> #include <cstring& ...
- 标准C程序设计七---104
Linux应用 编程深入 语言编程 标准C程序设计七---经典C11程序设计 以下内容为阅读: <标准C程序设计>(第7版) 作者 ...
- js 时间戳与yyyy-mm-dd或yyyy-MM-dd HH-mm-ss互相转换
首先是获取当前系统时间转换为时间戳 var timestamp = Date.parse(new Date());//获取当前时间 timestamp = timestamp / 1000; 然后是时 ...
- 洛谷——P2737 [USACO4.1]麦香牛块Beef McNuggets
https://www.luogu.org/problemnew/show/P2737 题目描述 农夫布朗的奶牛们正在进行斗争,因为它们听说麦当劳正在考虑引进一种新产品:麦香牛块.奶牛们正在想尽一切办 ...
- codevs——2181 田忌赛马
2181 田忌赛马 时间限制: 1 s 空间限制: 32000 KB 题目等级 : 钻石 Diamond 题解 题目描述 Description 中国古代的历史故事“田忌赛马”是为大 ...
- 会话跟踪技术Cookieless
会话跟踪技术Cookieless 在Web应用中,通常使用Cookie记录用户的状态,如用户名.访问时间等信息.当进行HTTP请求的时候,会自动发送Cookie信息给服务器.服务器接收到,就可以判 ...
- codeforces 979E(dp套dp)
题意: 有n个点,编号为1~n.有的点颜色是黑色,有的点颜色是白色,有的点的颜色待涂.你还可以连一些边,但这些边一定是从小编号连到大编号的点. 对于一个确定的图,我们去统计有多少条路径满足“该路径经过 ...
- down
Description 给出一个数列,求出这个序列的最长下降子序列的长度及方案数,子序列中的权值完全相同视为同一个序列 Input 第一行一个整数n,接下来一行n个整数表示序列的权值 Output 一 ...
- 前端必备性能知识 - http2.0
前端开发中,性能是一定绕不开的,今天就来说一下前后台通信中最重要的一个通道--HTTP2.0 最开始的通讯协议叫http1.0,作为始祖级的它,定义了最基本的数据结构,请求头和请求体,以及每一个字段的 ...