当前位置: 首页 > 编程日记 > 正文

字符串的模式匹配 (朴素模式匹配算法 ,KMP算法)

字符串的模式匹配

寻找字符串p在字符串t中首次出现的起始位置

字符串的顺序存储

typedef struct
{char str[MAXSIZE];int length;
}seqstring;

朴素的模式匹配算法

基本思想:用p中的每一个字符去与t中的字符一一比较。

模式p 正文 t

如果匹配成功,则返回p在t中首次出现的起始位置

如果匹配不成功则返回-1

最坏情况比较次数可达:(n-m+1)*m次

int index(seqstring p,seqstring t)
{int i,j,flag;i=0;flag=0;while((i<=t.length-p.length) && (!flag)){j=0;flag=1;while(j<p.length && flag){if(p.str[j]==t.str[i+j]){j++;}else{flag=0;}}++i;}if(flag){return (i-1);}else{	return -1;}
}

或者另一种朴素模式匹配算法

int index(seqstring t,seqstring p)
{//t是文本串,p是模式串int i=0,j=0;while(i<t.length && j<p.length){if(p.str[j]==t.str[i]){i++;j++;}else{i=i-j+1;//需要琢磨j=0;}}if(j==p.length){return (i-j);}else{return -1;}
}

在学习KMP算法之前:

真前缀:除了自身以外,一个字符串的全部头部组合

真后缀:除了自身以外,一个字符串的全部尾部组合

(注意区分 前缀 和 真前缀)

KMP算法的流程:

假设现在文本串S匹配到i位置,模式串P匹配到j位置

如果j==-1 或者 当前字符匹配成功,则i++,j++,继续匹配下一个字符

如果匹配失败,那么模式串向右移动的位数:失配字符所在位置-失配字符对应的next值。

这时:j=6,next[j]=2,所以向右移动 j-next[j]=6-2=4个位置。

或者基于《最大长度表》:以匹配字符数-失配字符的上一位字符所对应的最大长度值

这时:以匹配字符为6,失配字符的上一位字符所对应的最大长度值=2,所以向右移动 6-2=4个位置。

int KMP_search(seqstring s,seqstring p,int next[])//文本串S,模式串P
{int i=0,j=0;while(i<s.length && j<p.length){if(j==-1 || s.str[i]==p.str[j]){//如果j=-1或者当前字符匹配成功,即s[i]==p[j]i++;j++;}else{//当匹配失败时,模式串向右移动的位数为:失配字符所在位置-失配字符对应的next值j=next[j];}}if(j==p.length){return (i-j);}else{return -1;}
}

在KMP算法中有一个next数组相当关键

所以首要的是求出next数组的各个值

next数组的求解:

求解基于“真前缀”和“真后缀”,next[i]最长相同的前后缀的长度

根据《最大长度表》求解next[ ]数组

例:对于字符串ABCDABD

《最大长度表》

字符ABCDABD
最大前后缀公共元素长度0000120

next数组相当于最大长度表向后移一位,然后初始值赋值为-1。

可以不通过这个最大长度表直接计算next数组的值,即这个字符之前的字符串有多大长度的相同的前后缀(真前后缀)。

《next[] 数组》

i01234567
模式串ABCDABD\0
next[i]-10000120

i=0时,对于模式串的首位,我们统一为next[0]=-1

i=1时,前面的字符为A,其最长相同真前后缀长度为0,next[1]=0

i=2时,前面的字符为AB,其最长相同真前后缀长度为0,next[2]=0

i=3时,前面的字符为ABC,其最长相同真前后缀长度为0,next[3]=0

i=4时,前面的字符为ABCD,其最长相同真前后缀长度为0,next[4]=0

i=5时,前面的字符为ABCDA,其最长相同真前后缀长度为1,next[5]=1

i=6时,前面的字符为ABCDAB,其最长相同真前后缀长度为2,next[6]=2

i=7时,前面的字符为ABCDABD,其最长相同真前后缀长度为0,next[7]=0

下面需要思考的是:如果知道了next[ j ],怎么得到next [ j+1]呢?

next[ j ]=k,代表 j 之前的模式子串中,有长度为 k 的相同的前缀和后缀。

有了这个next[]数组,在KMP匹配中,当模式串 j 处失配时,模式串向右移动 j-next[j] 位。

1、对于模式串来说,如果p[ j ]==p[ k ],那么next[j+1]=next[ j ]+1 = k+1(p[ k ]是前缀,p[ j ]是后缀)

这里 k=2,j=6,p[k]=p[j],所以next[ j+1 ] = next[ j ]+1 = k+1 = 2+1 = 3。

2、如果p[ j ] != p[ k ],那么就说明 “p0p1...pk-1pk” 不等于 “pj-k...pj-1pj”。

这里j=2,k=6,ABC!=ABD,那么在字符E前没有长度为k+1的相同的前后缀,需要去找一个长度更短一些的前后缀。

思路:与上面的模式串与文本串匹配类似(与KMP思路类似),当p0p1p2...pj跟主串s0s1...si匹配时,如果在模式串j处失配,则模式串需要向右移动 j-next[j] 位,相当于 j = next [ j ]。

而现在是前缀与后缀匹配,"p0p1..pk-1pk"和"pj-k...pj-1pj"匹配时,发现在pk处匹配失败,若能在前缀"p0...pk-1pk"中不断递归前缀索引k=next[k],找到一个字符pk’==pj,且满足" p0pk'-1pk'  "==" pj-k'pj-1pj ",则最大相同的前后缀长度为k'+1,从而next[j+1]=k'+1=next[k']+1。否则继续递归k'=next[k'],直到next[k']=0。

void getnext(seqstring p,int next[])
{int i=0;int j=-1;next[0]=-1;while(i<p.length){if(j==-1 || p.str[j]==p.str[i]){++i;++j;next[i]=j;}else{j=next[j];}}printf("未优化:\n");for(i=0;i<p.length;i++){printf("%5d",next[i]);}printf("\n");return ;
}

next数组的优化

应用上面的KMP算法,以及next[]数组的求解算法得到next数组中的各个值。

在下面这个例子中,因为 ‘c’和‘b’不匹配,所以模式串右移j-next[j]=3-1=2位。右移了两位后,又是b与c的匹配,显然也是不匹配的。那么出现这种情况的原因是:p[j]=p[ next[j] ]。因为p[j]!=p[i],如果 p[ j ]==p[ next[ j ] ] ,那么必然会导致下一次匹配的失败,所以对求解next数组的函数进行进一步的优化。

void getnext(seqstring p,int next[])
{int i=0;int j=-1;next[0]=-1;while(i<p.length){//p[j]表示前缀,p[i]表示后缀if(j==-1 || p.str[j]==p.str[i]){++i;++j;//在前j+1个字符都匹配了以后,i+1,j+1,这时如果满足下面的if条件,则next[i]就是该字符之间的相同前后缀的长度if(p.str[j]!=p.str[i]){//如果两个不相同,就直接存储前面的子串的(以匹配的)长度next[i]=j;}else{//因为出现两个字符一样的情况,所以在使用KMP算法时,为了避免出现p[j]==p[next[j]]使算法效率变低,//所以当出现时需要递归,j=next[j]=next[next[j]].next[i]=next[j];}}else{j=next[j];}}printf("优化后:\n");for(i=0;i<p.length;i++){printf("%5d",next[i]);}printf("\n");return ;
}

只要出现了p[ next[ j ] ]=p[ j ]的情况,则把next[ j ]再次递归。例如在求模式串abab的next数组时,对于未优化的next数组,第二个a对应的值是0,相当于第二个a失配时,下一步匹配模式串会用p[ 0 ]处的a再次与文本串匹配,必然是失配的。所以再求第二个a的next值时,需要再次递归next[ 2 ]=next[ next[ 2 ] ]=next[0]=-1。此后,根据优化的新的next的值可知,第二个a失配时,执行" j==-1 || p.str[j]==p.str[i] ,++i,++j,继续匹配下一个字符 ",同理,对应的b的next的值为0。(可以自己手动推导,或者单步调试)。

对于优化后的next数组可以发现:如果模式串的后缀和前缀一样,例如abcdabcd,他们的前缀后缀都是abcd,其优化后的next数组是-1 0 0 0 -1 0 0 0,其前后缀的next值都是-1 0 0 0。

KMP算法的时间复杂度

回顾KMP算法的流程:

假设现在文本串S匹配到 i 的位置,模式串P匹配到 j 的位置

(1)如果j==-1 或者 S[ i ]==p[ j ],就 i++,j++,继续匹配下一个字符

(2)如果 j != -1 && 当前的字符匹配失配,i不变(即 i 不回溯),j = next [ j ],模式串向右移动了 j-next[ j ]位。

整个算法最坏的情况是:当模式串的首字符位于i-j的位置时才匹配成功

如果文本穿的长度为n,模式串的长度为m,匹配过程的时间复杂度是O(n),求解next数组是O(m),KMP算法的复杂度是O(m+n)。

有错的话,请大家及时指正!!!

参考文献:

1、 从头到尾彻底理解KMP(2014年8月22日版)

2、从头到尾彻底理解KMP(2014年7月版)

3、《数据结构(C语言版)》,李云清 杨庆红 揭安全编著

4、KMP算法(1):如何理解KMP

相关文章:

框架页面jquery装载

转载于:https://www.cnblogs.com/moonsoft/p/10313309.html

NDKJNI Android 相关资料整理(四)

JNI/NDK开发指南 JNI/NDK开发指南&#xff08;一&#xff09;—— JNI开发流程及HelloWorld http://blog.csdn.net/xyang81/article/details/41777471 JNI/NDK开发指南&#xff08;二&#xff09;——JVM查找java native方法的规则 http://blog.csdn.net/xyang81/article/det…

【ACM】与全排列相关的STL函数 prev_permutation next_permutation

排列 与 全排列 从n个不同元素中任取m&#xff08;m≤n&#xff09;个元素&#xff0c;按照一定的顺序排列起来&#xff0c;叫做从n个不同元素中取出m个元素的一个排列。 当mn时所有的排列情况叫全排列。如果这组数有n个&#xff0c;那么全排列数为n!个。 对于全排列的求解…

虚拟机无法使用网卡桥接模式

重装了好几遍操作系统&#xff0c;换了两个虚拟机&#xff0c;差点怀疑人生…… 最终的原因竟然是win10&#xff01;&#xff01;&#xff01; 1.执行WINR 输入services.msc&#xff0c;打开服务管理器&#xff08;回车&#xff09;。 2.找到Device Install Service服务 启动此…

Azure自动化部署运维浅谈

本次来谈一谈如何在Azure中实现一些简单的自动化运维的需求&#xff0c;一般来讲自动化运维我们通过很多第三方的工具平台实现&#xff0c;比较流行的目前有很多&#xff0c;比如老牌的chef, puppet,新兴的PowerShell DSC, ansible。这些应该都是耳熟能详的了。那么在Azure平台…

NDK/JNI demo ( 五 ) ORB_SLAM2在Android上的移植过程

Android平台搭建和NDK环境配置 Android移植基础 NDK是集成的Android中调用C代码的工具包&#xff0c;核心是JNI&#xff08;Java Native Interface&#xff09;技术&#xff0c;具体这里略过不表。只说说NDK开发的基本步骤&#xff1a; 1. 编写Java代码&#xff1a;在Java中定义…

[Nancy On .Net Core Docker] 轻量级的web框架

.net core现在已经有了大的发展&#xff0c;虽然笔者现在已经从事python开发&#xff0c;但是一直在关注.net的发展&#xff0c;在逛博客园的时候&#xff0c;发现有大家都会提到Nancy这个框架&#xff0c;在简单的使用之后&#xff0c;发现竟然是如此的简单而优雅 public clas…

【算法】【ACM】深入理解Dijkstra算法(单源最短路径算法)

Dijkstra算法是用来求解从某个源点到其他各顶点的最短路径&#xff08;单源最短路径&#xff09;。 下面的Dijkstra算法的讲解都是基于这个有向图&#xff0c;在遇到其他问题可以类比。 算法的基本思想&#xff1a; 把图中的定点分成两组&#xff0c;第一组包括已确定最短路径…

智能POS常见问题整理

智能POS预警值为小于所设的数量&#xff0c;H5就会变为锁定状态 智能POS查看数据库方法&#xff1a; 商米D1&#xff1a;设置-存储设备和USB-内部存储设备-浏览-winboxcash tablet.db为智能POS数据库 Winbox文件夹内&#xff0c;为相应logcat文件&#xff0c;应用出现问题时&am…

解决安卓系统写入SD卡权限问题

1.需要用户手动赋予的权限&#xff08; Dangerous Permissions&#xff09; 所属权限组权限日历READ_CALENDAR日历WRITE_CALENDAR相机CAMERA联系人READ_CONTACTS联系人WRITE_CONTACTS联系人GET_ACCOUNTS位置ACCESS_FINE_LOCATION位置ACCESS_COARSE_LOCATION麦克风RECORD_AUDIO…

【ACM】【STL】stack应用

C Stacks&#xff08;堆栈&#xff09; C Stack&#xff08;堆栈&#xff09; 是一个容器类的改编&#xff0c;为程序员提供了堆栈的全部功能&#xff0c;——也就是说实现了一个先进后出&#xff08;FILO&#xff09;的数据结构。 操作比较和分配堆栈empty()堆栈为空则返回真…

CHUCK手把手带你搞定OPENSTACK

以下是原文链接&#xff1a;http://blog.oldboyedu.com/openstack/转载于:https://blog.51cto.com/bovin/1858198

JVM基础面试题及原理讲解

2019独角兽企业重金招聘Python工程师标准>>> 本文从 JVM 结构入手&#xff0c;介绍了 Java 内存管理、对象创建、常量池等基础知识&#xff0c;对面试中 JVM 相关的基础题目进行了讲解。 写在前面&#xff08;常见面试题&#xff09; 基本问题 介绍下 Java 内存区域…

SLAM前端 ---------特征提取之ORB(ORB与SIFT与SURF)

ORB 论文翻译&#xff1a; 一种特征匹配替代方法&#xff1a;对比SIFT或SURF 1.ORB特征简介 ORB是Oriented FAST and Rotated BRIEF&#xff08;oFAST and rBRIEF&#xff09;的简称&#xff0c;ORB的名字已经说明了其来源&#xff0c;其实ORB特征是采用FAST方法来检测提取特…

oracle 内存分配和调优 总结

一直都想总结一下oracle内存调整方面的知识&#xff0c;最近正好优化一个数据库内存参数&#xff0c;查找一些资料并且google很多下。现在记录下来&#xff0c;做下备份。 一、概述&#xff1a; oracle 的内存可以按照共享和私有的角度分为系统全局区…

【ACM】Doubly Linked List(STL list)

题目链接&#xff1a;https://vjudge.net/problem/Aizu-ALDS1_3_C 这一题一开始的时候想的是用vector&#xff0c;超时 #include <iostream> #include <stack> #include <cstdio> #include <cstring> #include <queue> #include <vector>…

IOS获取焦点页面上移问题

var u navigator.userAgent; var flag; var myFunction; var isIOS !!u.match(/(i1;( U;)? CPU.Mac OS X/); if (isIOS) { document.body.addEventListener(focusin, () > { //软键盘弹起事件flag true;clearTimeout(myFunction); }) document.body.addEventListener(f…

SLAM之特征匹配(二)————RANSAC--------翻译以及经典RANSAC以及其相关的改进的算法小结

本文翻译自维基百科&#xff0c;英文原文地址是&#xff1a;http://en.wikipedia.org/wiki/ransac RANSAC是“RANdom SAmple Consensus&#xff08;随机抽样一致&#xff09;”的缩写。它可以从一组包含“局外点”的观测数据集中&#xff0c;通过迭代方式估计数学模型的参数…

【ACM】树 小结

树是一种表达层级结构的数据结构&#xff0c;也是实现高效算法与数据结构的基础。 学习之前的基础&#xff1a;数组&#xff0c;循环处理&#xff0c;结构体&#xff0c;递归函数。 树&#xff1a;由结点&#xff08;node&#xff09;和连接结点的边&#xff08;edge&#xf…

【cocos2d-js官方文档】九、cc.loader

概述 原来的cc.Loader被改造为一个单例cc.loader&#xff0c;采用了插件机制设计&#xff0c;让loader做更纯粹的事。 各种资源类型的loader可以在外部注册进来&#xff0c;而不是直接将所有的代码杂揉在cc.Loader中&#xff0c;更好的方便管理以及用户自定义loader的创建。 cc…

更换VC后DDC提示证书不可用

问题描述&#xff1a;客户环境由Windows VC更换成Linux VC后&#xff0c;DDC提示证书不可用问题原因&#xff1a;因为VC更换后&#xff0c;存储在DDC数据库HostingUnitServiceSchema.HypervisorConnectionSSLThumbprint表中证书指纹信息和新得VC证书指纹信息不匹配。解决方法&a…

尺度空间理论与图像金字塔

我们之所以决定使用卷积后的特征是因为图像具有一种“静态性”的属性。也就是说使用卷积就是为了提取显著特征&#xff0c;减少特征维数&#xff0c;减少计算量。 在对图像进行卷积操作后的只管现象&#xff1a;图像变得模糊了&#xff0c;可是我们依然能够分辨出是什么&#x…

【ACM】 multiset 的 一些应用

一、The kth great number 题目链接&#xff1a;https://vjudge.net/problem/HDU-4006 用set写超时 &#xff08;在VJ里&#xff0c;用C显示Compilation Error&#xff0c;选择G&#xff0c;则是TLE&#xff09; #include <iostream> #include <set> #include &…

apache2.2 做后端,增加真实ip到日志中

apache2.2使用mod_remoteip模块 一.安装 wget https://github.com/ttkzw/mod_remoteip-httpd22/raw/master/mod_remoteip.c/usr/local/apache/bin/apxs -i -c -n mod_remoteip.so mod_remoteip.c 二.添加Apache配置vi /usr/local/apache/conf/httpd.confInclude conf/extra/htt…

高可用方案系统架构

2019独角兽企业重金招聘Python工程师标准>>> 高可用方案 系统架构 转载于:https://my.oschina.net/qiongtaoli/blog/3007587

OpenCV3.2.0+VS2017在window10开发环境配置记录

本机环境&#xff1a;win10 64位 OpenCV3.2.0 Visual Studio 2017 最后结果&#xff0c;亲测可用OpenCV官方下载地址&#xff1a; http://opencv.org/releases.html#本人选择opencv3.2.0基于Windows平台。读者根据自己需要选择合适版本及平台下载。 选择window版本的opencv下载…

C++vector迭代器失效的问题

转载:http://blog.csdn.net/olanmomo/article/details/38420907 转载:http://blog.csdn.net/stpeace/article/details/46507451 转载:http://www.cnblogs.com/xkfz007/articles/2509433.html 转载:http://www.cnblogs.com/BeyondAnyTime/archive/2012/08/13/2636375.html 有这样…

【HDU】1251统计难题 (字典树:二维数组,结构体数组,链表,map)

使用二维数组或者结构体数组都可以&#xff0c;但是在计数的时候有一点点小区别 一、结构体数组 #include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <string> typedef long long ll; using namespace…

Jmeter干货 不常用却极其有用的几个地方

1. Jmeter测试计划下Run Thread Groups consecutively 表示序列化执行测试计划下所有线程组中的各个请求 如下图配置&#xff0c;新建的测试计划中&#xff0c;不默认勾选此项&#xff0c; 而享用Jmeter做接口自动化测试的同学们&#xff0c;会发现一个问题是&#xff0c;可能多…

图像滤波总结(面试经验总结)

目录 图像平滑处理&#xff0c;6种滤波总结的综合示例 【盒式滤波、均值滤波、高斯滤波、中值滤波、双边滤波导向滤波】 1-图像滤波 2-代码演示 3-显示结果 4-程序说明 5 角点检测&#xff08;Harris,Fast,surf&#xff09; 图像平滑处理&#xff0c;6种滤波总结的综合示…