模式匹配是查找的一种,分为单模式匹配和多模式匹配。查找,就是在一个集合中查找一个或多个元素,查找一个元素就叫单模式匹配,查找多个元素就是多模式匹配,这里只探讨单模式匹配。虽然模式匹配看上去与数字的查找不一样,但是本质上任然是一种查找,比如在“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)的更多相关文章

  1. 串的模式匹配和KMP算法

    在对字符串的操作中,我们经常要用到子串的查找功能,我们称子串为模式串,模式串在主串中的查找过程我们成为模式匹配,KMP算法就是一个高效的模式匹配算法.KMP算法是蛮力算法的一种改进,下面我们先来介绍蛮 ...

  2. 【模式匹配】KMP算法的来龙去脉

    1. 引言 字符串匹配是极为常见的一种模式匹配.简单地说,就是判断主串\(T\)中是否出现该模式串\(P\),即\(P\)为\(T\)的子串.特别地,定义主串为\(T[0 \dots n-1]\),模 ...

  3. 模式匹配的KMP算法详解

    这种由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现的改进的模式匹配算法简称为KMP算法.大概学过信息学的都知道,是个比较难理解的算法,今天特把它搞个彻彻底底明明白白. 注意到这 ...

  4. 3小时搞定一个简单的MIS系统案例Northwind,有视频、有源代码下载、有真相

    一.瞎扯框架.架构 楼主自从1998年从C语言.MASM.Foxbase开始学计算机开始接触这个行当16年以来,2001年干第一份与程序.软件.然后是各种屌的东西开始,差不多干了13年了,这13年来, ...

  5. 字符串模式匹配之KMP算法图解与 next 数组原理和实现方案

    之前说到,朴素的匹配,每趟比较,都要回溯主串的指针,费事.则 KMP 就是对朴素匹配的一种改进.正好复习一下. KMP 算法其改进思想在于: 每当一趟匹配过程中出现字符比较不相等时,不需要回溯主串的 ...

  6. 简单kmp算法(poj3461)

    题目简述: 给你两个字符串p和s,求出p在s中出现的次数. 思路简述: 在介绍看BF算法时,终于了解到了大名鼎鼎的KMP算法,结果属于KMP从入门到放弃系列,后来看了几位大神的博客,似乎有点懂了.此题 ...

  7. HDU 2087 剪花布条 (简单KMP或者暴力)

    剪花布条 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  8. 字符串模式匹配之KMP算法的next数组详解与C++实现

    相信来看next数组如何求解的童鞋已经对KMP算法是怎么回事有了一定的了解,这里就不再赘述,附上一个链接吧:https://www.cnblogs.com/c-cloud/p/3224788.html ...

  9. 学渣乱搞系列之扩展KMP的那点事

    扩展KMP牵涉了一些相对运动的姿势,比较费解!本学渣看了一天的扩展KMP,打算写点东西...本文看后,出现的后果本人一概不负责.毕竟我不是很会表达. 扩展KMP是搞什么灰机的?本学渣所知道的扩展KMP ...

随机推荐

  1. 删除Python UserWarning[已解决]

    在使用MySQLdb包后,导入测试时发现一个警告. /usr/lib/python2.6/site-packages/setuptools-0.8-py2.6.egg/pkg_resources.py ...

  2. SQL点滴15—在SQL Server 2008中调用C#程序

    原文:SQL点滴15-在SQL Server 2008中调用C#程序 T-SQL的在执行普通的查询的时候是很高效的,但是在执行循环,判断这样的语句的时候效率就不那么的高了.这时可以借助CLR了,我们可 ...

  3. LeetCode之Min Stack

    1.原文问题描述: Design a stack that supports push, pop, top, and retrieving the minimum element in constan ...

  4. qsort 排序功能 总结

    qsort包括在<stdlib.h>头文件里.此函数依据你给的比較条件进行高速排序,通过指针移动实现排序. 排序之后的结果仍然放在原数组中.使用qsort函数必须自己写一个比較函数. 函数 ...

  5. Android-往来:包Group添加到联系人

    long groupId = Constant.createGroupId; ContentValues values = new ContentValues(); for(int i=0,len=C ...

  6. python进程池剖析(二)

    之前文章中介绍了python中multiprocessing模块中自带的进程池Pool,并对进程池中的数据结构和各个线程之间的合作关系进行了简单分析,这节来看下客户端如何对向进程池分配任务,并获取结果 ...

  7. iis处理请求随记回顾

    ----http是无状态的, 每次http请求户不影响,都是独立的:不会记的上次请求: -------iis原理:输入地址--socket封装请求体报文--发送---iis解析封装响应体---返回: ...

  8. SpringMVC Hibernate+Spring+Spring MVC+Bootstrap的管理系统实现

    SpringMVC学习系列(12) 完结篇 之 基于Hibernate+Spring+Spring MVC+Bootstrap的管理系统实现 到这里已经写到第12篇了,前11篇基本上把Spring M ...

  9. 如何在网页启动Windows服务

    由于公司有许多windows服务进行业务的处理,所以对服务的维护也是一个比较头痛的问题,因为自己也不知道服务什么时候自动停了,而且更主要的原因是服务都是由运维部门在维护管理,开发这边没有直接操作服务的 ...

  10. CSDN 高校俱乐部: 排列搜索

    CSDN 高校俱乐部/英雄会 题目: 设数组a包含n个元素恰好是0..n - 1的一个排列,给定b[0],b[1],b[2],b[3]问有多少个0..n-1的排列a,满足(a[a[b[0]]]*b[0 ...