搞定单模式匹配(简单,KMP)
模式匹配是查找的一种,分为单模式匹配和多模式匹配。查找,就是在一个集合中查找一个或多个元素,查找一个元素就叫单模式匹配,查找多个元素就是多模式匹配,这里只探讨单模式匹配。虽然模式匹配看上去与数字的查找不一样,但是本质上任然是一种查找,比如在“aabaabaabaac”中查找“aabaac”,对计算机来说,处理的仍然是在集合{aabaab, abaaba, baabaa, aabaab, abaaba, baabaa, aabaac}中查找“aabaaac”,这是计算机的流处理特性决定的。所谓的简单模式匹配算法,就是顺序查找,代码如下:
#include<stdio.h>
#include<string.h>
int searchstr(char S[],char T[])
{
int i=,j=;
while(i<=strlen(S) && j<=strlen(T))// i 或 j 越界跳出
{
if(S[i-]==T[j-]) {++i,++j;}//同则后移
else {i=i-j+;j=;}//异则结束本次比较,准备下一次比较
}
if(j>strlen(T)) return i-j+;//判断跳出类型, i 还是 j 越界跳出
else return ;
}
main()
{
char S[]="aabaabaabaac";
char T[]="aabaac";
int temp=searchstr(S,T);
if(temp) printf("the position is : %d\n",temp);
else printf("ERROR!\n");
}
KMP算法并没有盲目搜索,而是做了一些逻辑推理跳过了一些不必要的搜索,下面就演示一下KMP是如何推理的,我们以在“aabaabaabaac”中查找“aabaac”为例,假设对比到第六位发现不匹配,这时前面5位都是匹配的。

那么我们结束这次对比,进行下一次对比,如下图:

“aabaa?”匹配失败,我们跳过了“abaaa??”与“baa???”这两次对比直接来到了“aa????”,为什么会这样,简单推理一下就知道了,模版是“aabaac”,很显然“abaaa??”与“baa???”都不可能匹配,只有“aa????”才【有可能】与模版匹配。事实上这个可能匹配的字符串是有一定特征的,匹配失败时我们得到aabaa这个相同的前缀,aabaa有若干前缀和若干后缀(规定缀不能为字符串本身,aabaa不是aabaa的缀),前后缀相同的有a和aa,我们称之为同缀,我们只找最长同缀aa,它的长度对我们很重要。接下来回到“aa????”这次对比:

“aa????”和模版“aabaac”对比,最长同缀aa根本不用比,直接从红色的部分开始对比,因为i本来就是指在这里的,所以i看上去是不动的,只用重定位j就行了,这个j的新位置就是书上说的next数组,next[j-1]表示第j位对比失败后,j跳转的位置。在本例中第6位对比失败,j跳转到3,即next[5]中存储的是3。3是如何推理出来的?其实就是【1+最长同缀的长度】。现在假设是“aab?”第四位匹配失败,那么相同前缀是aab,aab没有最长同缀,所以它的最长同缀长度是0,按照规律j重定位到1+0=1位,即新一次对比的第一位,这就是为什么要加1的原因。当第一位匹配失败时,next数组不起任何作用,但我们还是象征性的把next[0]设为0,它表示第一位对比失败,当检测到j为0,我们需要把i和j都后移一位,这样j就刚好到1了,于是任何模版的next[0]都为0。任意字符X的最长同缀是0,于是任何模版的next[1]都为1。
为什么KMP这么不好理解,原因在于书本上没有说清楚两次逻辑推理过程,第一次推理是直接跳过若干次比较来到新一次的比较的第一位,第二次推理是在新一次的对比中,又跳过了前面若干位的比较,最终定位到正确的位置。这个过程中i并没有变,而j按照某种规律重新定位了,这个规律就是【j=1+最长同缀的长度】。毫无疑问,next数组是根据模版推理出来的,所以需要事先进行计算,我们假设next已经求解,KMP代码如下(其代码与简单匹配的代码高度类似):
int KMP(char S[],char T[],int next[],int pos)
{
int i=pos,j=;//添加功能,从 S 中的pos位置开始查找
while(i<=strlen(S) && j<=strlen(T))// i 或 j 越界跳出
{
if(j== || S[i-]==T[j-]) {++i,++j;}//第一位对比失败和任意位对比成功都处理成i,j同时后移
else {j=next[j-];}//按照某种规律重定位 j
}
if(j>strlen(T)) return i-j+;//判断跳出类型, i 还是 j 越界跳出
else return ;
}
KMP函数写出来之后,不要以为就万事大吉了,离实现KMP算法还远着呢,整个KMP算法的精髓就在于推理next数组,如何推理next数组?这个时候我们就要有分治思想了。





看看我们以上在做什么?我们在枚举7长度字符串可能的next值,它的取值可能是next[6]+1到0。但是它看上去就像是在“aba?”中查找“abaa”,注意此时“abaa”的next数组已经求解出来了,当第四位匹配出错,我们跳过了:

直接来到了:

其实我们还可以再跳过几个字符:

这个过程是不是似曾相识呢?没错,这就是KMP的重定位j,因为abaa的next已经求解,所以我们确实是可以使用next数组的。我们注意到在匹配成功之前,i始终是不动的,i的含义其实就是“正在求解i长度字符串的next[i]”。而匹配成功时j的含义就是“i长度字符串的最长同缀”。此时j+1就是next[i]值,然后我们把i,j同时后移处理i+1长度字符串最长同缀。j=0,没有啥用,它仅仅做为一个标志,标志找不到最长同缀,即最长同缀为0。根据这些我们就可以完成getnext代码,代码如下:
void getnext(char T[],int next[])
{
next[]=;
next[]=;//没必要求解,恒成立
int i=,j=;//直接从求解2长度字符串开始
while(i<=strlen(T))
{
if(j==||T[i-]==T[j-])//字符相等标志找到最长同缀,j 为 0 标志最长同缀为 0
{
next[i]=j+;// i 的含义是 i 长度字符串,j的含义是最长同缀为 j
++i;++j;//i,j整体后移,表示已经求解i长度字符串,下一次将处理i+1长度字符串
}
else j=next[j-];//j重定位
}
}
充分理解了这段代码之后,我们还可以把它做的更简洁:
void getnext(char T[],int next[])
{
next[]=;
next[]=;
int i=,j=;
while(i<=strlen(T))
{
if(j==||T[i-]==T[j-]) next[i++]=++j;//使用跟新前的 i 值和跟新后的 j 值
else j=next[j-];
}
}
全部KMP代码如下:
#include<stdio.h>
#include<string.h>
#define MAXSIZE 100
int KMP(char S[],char T[],int next[],int pos)
{
int i=pos,j=;//添加功能,从 S 中的pos位置开始查找
while(i<=strlen(S) && j<=strlen(T))// i 或 j 越界跳出
{
if(j== || S[i-]==T[j-]) {++i,++j;}//第一位对比失败和任意位对比成功都处理成i,j同时后移
else {j=next[j-];}//按照某种规律重定位 j
}
if(j>strlen(T)) return i-j+;//判断跳出类型, i 还是 j 越界跳出
else return ;
} void getnext(char T[],int next[])
{
next[]=;
next[]=;
int i=,j=;
while(i<=strlen(T))
{
if(j==||T[i-]==T[j-]) next[i++]=++j;//使用跟新前的 i 值和跟新后的 j 值
else j=next[j-];
}
}
main()
{
char S[]="vfyabaababm";
char T[]="abaababm";
//计算 next 数组
int next[MAXSIZE];
getnext(T,next);
//显示 next 数组
printf("the next array is : ");
for(int i=;i<strlen(T);i++)
printf("%d ",next[i]);
printf("\n");
//显示查找结果
int temp=KMP(S,T,next,);
if(temp) printf("the position is : %d\n",temp);
else printf("ERROR!\n");
}
搞定单模式匹配(简单,KMP)的更多相关文章
- 串的模式匹配和KMP算法
在对字符串的操作中,我们经常要用到子串的查找功能,我们称子串为模式串,模式串在主串中的查找过程我们成为模式匹配,KMP算法就是一个高效的模式匹配算法.KMP算法是蛮力算法的一种改进,下面我们先来介绍蛮 ...
- 【模式匹配】KMP算法的来龙去脉
1. 引言 字符串匹配是极为常见的一种模式匹配.简单地说,就是判断主串\(T\)中是否出现该模式串\(P\),即\(P\)为\(T\)的子串.特别地,定义主串为\(T[0 \dots n-1]\),模 ...
- 模式匹配的KMP算法详解
这种由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现的改进的模式匹配算法简称为KMP算法.大概学过信息学的都知道,是个比较难理解的算法,今天特把它搞个彻彻底底明明白白. 注意到这 ...
- 3小时搞定一个简单的MIS系统案例Northwind,有视频、有源代码下载、有真相
一.瞎扯框架.架构 楼主自从1998年从C语言.MASM.Foxbase开始学计算机开始接触这个行当16年以来,2001年干第一份与程序.软件.然后是各种屌的东西开始,差不多干了13年了,这13年来, ...
- 字符串模式匹配之KMP算法图解与 next 数组原理和实现方案
之前说到,朴素的匹配,每趟比较,都要回溯主串的指针,费事.则 KMP 就是对朴素匹配的一种改进.正好复习一下. KMP 算法其改进思想在于: 每当一趟匹配过程中出现字符比较不相等时,不需要回溯主串的 ...
- 简单kmp算法(poj3461)
题目简述: 给你两个字符串p和s,求出p在s中出现的次数. 思路简述: 在介绍看BF算法时,终于了解到了大名鼎鼎的KMP算法,结果属于KMP从入门到放弃系列,后来看了几位大神的博客,似乎有点懂了.此题 ...
- HDU 2087 剪花布条 (简单KMP或者暴力)
剪花布条 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...
- 字符串模式匹配之KMP算法的next数组详解与C++实现
相信来看next数组如何求解的童鞋已经对KMP算法是怎么回事有了一定的了解,这里就不再赘述,附上一个链接吧:https://www.cnblogs.com/c-cloud/p/3224788.html ...
- 学渣乱搞系列之扩展KMP的那点事
扩展KMP牵涉了一些相对运动的姿势,比较费解!本学渣看了一天的扩展KMP,打算写点东西...本文看后,出现的后果本人一概不负责.毕竟我不是很会表达. 扩展KMP是搞什么灰机的?本学渣所知道的扩展KMP ...
随机推荐
- Java 多并发之原子访问(Atomic Access)
在编程中,一个原子操作是只会出现一次的.一个原子操作在中间不会停止:要么全部发生要么一点也不发生.我们只有在原子操作完成之后才会看到原子操作的具体影响. 甚至是非常简单的表达式能够构造分解为简单操作的 ...
- C#如何获得系统时间
原文:C#如何获得系统时间 C#获取时间,日期 //C#里内置的DateTime基本上都可以实现这些功能,巧用DateTime会使你处理这些事来变轻松多了 //今天 ...
- visual studio code, asp.net5, mvc6资料汇总
最近在试探性地跟随微软最新发布的一些产品,现列下某些挺好的文章和链接 code.visualstudio.com http://blogs.msdn.com/b/cesardelatorre/arch ...
- Operating System 概述和学习图
Operating System 概述和学习图 大神绕道,鄙人初入 OS . 一.想知OS,先知计算机系统概述 #图解 #基本指令和中断周期 #直接内存存取(Direct Memory Access, ...
- [转]loadView的用法,loadView创建基本界面,DidLoad读入数据
loadview: // 有没有nib 只要是复写了loadview loadview都会被执行 有nib文件的话加载的是nib文件的view 没有的话会按照loadview里的代码加载 ...
- Python Learing(一):Basic Grammar
装了python2.7,去图书馆借了python入门经典,暂且简单写下学习笔记,以供自己回忆 学习笔记(一)主要是基础语法部分: 1.python中数字以及字符串的使用; 2.输入与输出语句; 3.逻 ...
- Introsort(内观排序)
.NET 4.5 这个版本的Array.Sort更改了STL的内观排序算法,那相对于快速排序内观排序到底有什么优化过的呢? 根据维基百科所说,这个排序算法首先从快速排序开始,当递归深度超过一定深度(深 ...
- 使用 ASP.NET MVC 4, EF, Knockoutjs and Bootstrap 设计和开发站点 - 6 - 业务逻辑
翻译:使用 ASP.NET MVC 4, EF, Knockoutjs and Bootstrap 设计和开发站点 - 6 - 业务逻辑 Part 3: 设计逻辑层:核心开发 如前所述,我们的解决方案 ...
- MVC视图与控制器分离简单描述
一,控制器 CheckIndexAreaRegistration.cs public class CheckIndexAreaRegistration : AreaRegistration { pub ...
- 【转】title与alt的区别
html中的title属性和alt属性让人有些混淆. 以前不知道有title这个属性,第一次用到它时,就和alt产生了混淆.一位朋友告诉我说,alt是图片img标签里用的,title是超链接里用的,当 ...