5000字详解KMP算法
目录
KMP算法是什么?
暴力求解:
KMP算法原理:
建立next数组
代码实现next数组
next数组的优化
KMP算法的具体代码实现(C)
KMP算法是什么?
引自百度百科:
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)。
注 :此文的haystack代表的是主串,needle代表的是子串
暴力求解:
暴力求解就是分别定义 两个指针 i 和 j 分别指向两个字符串的首字符,cur记录的是只一次比较的时候 i 的其实位置,所以,在上图中,当 i 和 j 指向的字符串不相同的时候,j 回到子串的起始位置,i 回到cur的位置后进行自增的运算,然后把 i 的位置赋值给cur,记录下这一次比较的起始为位置。所以此时的比较次数为两个字符串的长度的积,即m*n;
C实现具体代码
int strStr(char * haystack, char * needle){if (!*needle)return 0;int lenStr = strlen(haystack);int lenSub = strlen(needle);if (lenStr < lenSub)return -1;int i = 0;int j = 0;int cur = i;while (i < lenStr && j < lenSub){if (haystack[i] == needle[j]){i++;j++;}else{i = ++cur;j = 0;}}if (j >= lenSub)return i - j;return -1;
}
KMP算法原理:
KMP算法不像暴力求解,当发现字符串不相等的时候,i 是不需要返回的,只需要 j 返回到某一个特定的位置,并从某一个位置直接开始比较,此时字符串比较的时间复杂度降为了O(m+n);
怎么确定 j 返回的具体位置呢?
定义 i 和 j 分别指向主串和子串的起始位置;当 i 和 j 指向第6个元素的时候,两个元素是不相等的,但是 指针 i 是不能进行返回操作的,而 j 是返回到某一个特定的位置,怎么求 j 返回的位置?看下图:
很明显,当 i 和 j 能比较到第6个元素,说明前5个元素都是相同的,如果在子串中找到两个区间(区间a(从0到 x1 )能够和区间 b(从 x2 到 j-1))是相同的,那么主串中的一定存在一个区间c(从 x3 到 i-1)和区间 a 是相同的;在上图中;区间 a是[0,1],区间 b[3,4],区间 c是[3,4](主串中的)。 所以这个时候 j 应该回到子串中 ‘ 2 ’ 这个位置,即子串中的第三个元素' c '。
分析:因为主串和子串的前n-1个都是相同的,所以如果在子串中能够找到一对最长的相同前缀和后缀,则主串中存在一块相同区间大小的后缀就和子串的前缀是相同的,此时,子串中的‘ j ’只要回到前缀的后一位就行了。因为子串的前缀已经和主串的后缀的相同。在丄同中,‘ j ’将会到第三个元素后开始和‘ i ’指向的元素进行比较;如果还是不相同,就继续以刚才的方法进行返回,直到找到为止或者触发了结束条件。
现在,我们已经知道‘ j ’该返回到哪一个位置,但是,‘ j ’在子串的位置我们是不知道的,它可能在子串的第1/2/3/4/……n的位置,那我们该怎么确定‘ j ’的返回位置呢?
建立next数组
为了确定‘ j ’在不同位置的返回值,我们建立一个next数组,将‘ j ’(j表示子串在某一个位置和主串的字符不能匹配)在不同位置时要返回到特定位置存放在数组中;给定一个子串(为了方便演示,该子串不同于之前的子串)
我们将next数组的第一个元素设为-1;而next[1]必为0;因为当字符串匹配到第二个元素不相同的时候,那么该元素前面只有一个元素(一个元素不构成相同的前缀和后缀);
当主串和子串匹配到第三个元素(‘ c ’)不相同时,该元素前面之后元素‘ a ’和‘ b ’,不存在相同的前后缀,所以next[2]=0;
当主串和子串匹配到第四个元素(‘ a ’)不相同时,‘ a ’,‘ b ’,‘ c ’,也够不成相同的前后缀;所以next[3]=0;
当主串和子串匹配到第五个元素(‘ b ’)不相同时,此时前四个元素‘ a ’,' b ',' c ',' a '中,存在前缀‘ a ’和后缀‘ a ’构成相同的前后缀,前后缀的长度为1,那么此时next[j]=1;
同样的,当主串和子串匹配到第八个元素(‘ d ’)不相同时,此时存在前缀“abca”和后缀“abca”是相同的(这里的前缀后缀共用了一个‘ a ’,这种情况相对比较特殊,但也算);此时next[7]=4;
当主串和子串匹配到第九个元素(‘ a ’)不相同时;不存在区间a(0到x1)和区间b(x2到 j-1,此时的 j==8);所以next[8]=0;
为了鉴别自己是否掌握next数组的建立;请完成下面的一个小测试
给定子串,求其next数组
答案:(如果还是不会,请仔细推敲前面构建next数组的过程)3
既然我们解决如何构建next数组,那我们可以走下下一步------->>>代码实现next数组
代码实现next数组
以下图的子串为例 :
3
我们先来做一个假设:next[ j-1 ]==k;
next[ j ]==k说明在字串中存在区间a(0到k-1)和区间b(x到 j-1);肯定会有好兄弟问为什么是k-1;因为next[ j-1 ]==k,则说明在子串中存在一对相同的前后缀,长度为k,而0又是第一个元素,所以为k-1;
因为前后缀向同,则 j-1-x=k-1-0------>>>>x=j-k;
所以存在区间a(0到 k-1)和区间b( j-k 到 j-1)是相同的
即: needle[0]……needle[k-1]==needle[j-k]……needle[j-1],两边的长度均为K;
如果此时存在needle[ j ]==needle[ k ];
那么存在: needle[0]……needle[k-1] needle[ k ]==needle[j-k]……needle[j-1] needle[ k ]; 此时前后缀的长度为k+1;
如果此时needle[ j ] !=needle[ k ];
当指针‘ j ’指向第八个元素的时候,此时k==4,needle[ 7 ] != needle[ 4 ];此时的不存在一对前后缀是相同的;那么,就要跳过前一个元素的后缀取找相同的前后缀(前一个元素是d(下标 7),他的后缀为“abca”);所以将next[k]赋值给 k(此时k==4),然后比较此时的next[ k ]和needle[ j ];如果相等,将次时代 k +1,如果不相等,继续将next[ k ] 赋值为 k;(此时k==1); 而needle[ k ]任然不等于needle [ j ];继续将next[ k ]赋值给k(此时k==-1);说明不存在相同的前缀,则进行 k+1操作得到k==0;
将上述过程用代码实现
void Getnext(int* next, char* needle, int len)
{if (len == 1){next[0] = -1;//如果子串只有一个元素}else{next[0] = -1;next[1] = 0;int i = 2;int k = 0;//表示next的第i-1项k=next[i-1],此时的i为2;即你next[1]==0;while (i < len){if (k == -1 || Sub[i - 1] == needle[k])//k==-1说明没有了相同的前后缀了{next[i] = k + 1;i++;k = next[i-1];//因为此时的 i 变了,k 也要跟着变;}else{k = next[k];//}}
}
next数组的优化
来看下面的一个子串:
当字符串匹配到不相同的时候,要进行 j =next [ j ]的操作(即将 j 返回到某一位置);
如果此时存在 needle [ j ]==needle[ next[ j ] ];说明如果会退到的位置和当前的字符一样,那么next在当前的位置处的值和needle[ next[ j ] ]位置的next值一样。
优化后的代码
void Getnext(int* next, char* Sub, int len)
{if (len == 1){next[0] = -1;}else{next[0] = -1;next[1] = 0;int i = 2;int k = 0;//表示next的第i-1项k=next[i-1],此时的i为2;即你next[1]==0;while (i < len){if (k == -1 || Sub[i - 1] == Sub[k]){next[i] = k + 1;i++;k = next[i-1];}else{k = next[k];}}//优化next数组for(int j=1;j<len;j++){if(Sub[j]==Sub[next[j]])k=next[next[j]];}}
}
KMP算法的具体代码实现(C)
void Getnext(int* next, char* Sub, int len)
{if (len == 1){next[0] = -1;}else{next[0] = -1;next[1] = 0;int i = 2;int k = 0;//表示next的第i-1项k=next[i-1],此时的i为2;即你next[1]==0;while (i < len){if (k == -1 || Sub[i - 1] == Sub[k]){next[i] = k + 1;i++;k = next[i-1];}else{k = next[k];}}//优化next数组for(int j=1;j<len;j++){if(Sub[j]==Sub[next[j]])k=next[next[j]];}}
}
int strStr(char * haystack, char * needle){if (!*needle)return 0;int lenStr = strlen(haystack);int lenSub = strlen(needle);if (lenStr < lenSub)return -1;int i = 0;int j = 0;int* next = (int*)malloc(sizeof(int) * lenSub);Getnext(next, needle, lenSub);while (i < lenStr && j < lenSub){if (j==-1||haystack[i] == needle[j])//j==-1说明它回退到了起始位置,这是的j如果不++会//越界{i++;j++;}else{j = next[j];}}if (j >= lenSub){free(next);//要释放开辟的内存return i - j;}free(next);return -1;
}
5000字详解KMP算法相关推荐
- 【数据结构】详解KMP算法
字符串匹配算法:简单来说就是给你一个主串和一个子串,让你查找子串在主串中的位置,找到返回下标. 常见的两种算法:BF算法.KMP算法 这两种算法是怎样的思路呢,我们接着往下看: 目录 BF算法(暴力算 ...
- 【数据结构-查找】2.字符串(逐步演绎过程,超级详解KMP算法)
串的定义 串(string)是有0~n个字符组成的有限序列,一般记为 S=′a1a2-an′(n≥0)S = 'a_1a_2-a_n'(n≥0) S=′a1a2-an′(n≥0) S 是字符串的 ...
- 详解KMP算法原理,以及完整java与C++实现
点击此处学习更多算法与通信知识 作者 | labuladong 来源 | labuladong KMP 算法(Knuth-Morris-Pratt 算法)是一个著名的字符串匹配算法,效率很高,但是确实 ...
- 数据结构:详解KMP算法,手工求解next、nextval数组,求模式串的比较次数例题
KMP 算法 手工求解 next 数组,nextval数组 例题:求模式串的比较次数 2019 年 408 统考真题 设主串 T="abaabaabcabaabc",模式串 S=& ...
- 5000字详解数据安全治理
2010年,针对数据安全治理,微软提出了专门强调隐私.保密和合规的数据安全治理框架(DGPC),希望企业和组织能够以统一的跨学科的方式来实现目标,而非组织内不同部门独立实现.DGPC框架能够与企业现有 ...
- 一等奖,iPhone XR来了!A/B Test,个人中心首页改版实验分析报告(5000字详解)...
作者:JM,从0到1规划过访客日浏览量超千万的流量分析系统,曾为年销售破百亿跨境电商公司商业分析系统的产品负责人,现为广告营销公司数据产品经理. 关键词:A/B Test 工具:友盟+移动统计(U-A ...
- 【算法知识】详解堆排序算法
点击蓝色字关注我们! 什么是堆 「堆」首先是一个完全二叉树,「堆」分为「大顶堆」和「小顶堆」: 「大顶堆」 : 每个节点的值大于或等于其左右孩子节点的值,称为大顶堆. 「小顶堆」同理就是每个节点的值小 ...
- 详解rsync算法--如何减少同步文件时的网络传输量
详解rsync算法--如何减少同步文件时的网络传输量 先看下图中的场景,客户端A和B,以及服务器server都保存了同一个文件,最初,A.B和server上的文件内容都是相同的(记为File.1).某 ...
- 【算法知识】详解基数排序算法
已发布: [算法知识]详解选择冒泡算法 [算法知识]详解选择排序算法 [算法知识]详解插入排序算法 [算法知识]详解快速排序算法 [算法知识]详解归并排序算法 基本思想 基数排序的思想是将整数按位数切 ...
最新文章
- linux 删除mysql
- 新网 云服务器,新网云服务器的优势包括什么?
- 弹出页(指定高度,自由拖动,点击空白包括状态栏触发)
- sm4加密 解密(oc)
- 上海事业编制 计算机 待遇怎么样,事业单位情况
- MyBatis源码解析(十二)——binding绑定模块之MapperRegisty
- Linux 内存中的Cache
- 【转】数据库范式(1NF 2NF 3NF BCNF)详解二
- ArcGIS pro 发布地图服务(一)动态地图服务
- 三端稳压管型号选型对照大全-KIA半导体
- 用python获取实时地球图像作为壁纸(windows)
- 自适应音频功率放大器
- android 如何进入安全模式,手机怎么进入安全模式
- 第一章 引论 -- 项目管理知识体系指南(PMBOK指南)(第五版)
- HTTP协议和URLConnection使用
- 什么是SQL注入?怎么解决SQL注入?
- ftp服务器修改pasv,ftp服务器修改pasv
- 计蒜客 17115 Coin(2017 ACM-ICPC 亚洲区(西安赛区)网络赛 B)
- 服务器性能测试综合实验报告,CPU性能测试实验报告.doc
- c一些基本算法和公式
热门文章
- java web乱码及解决方法
- uIP tcp/ip协议分析及其在嵌入式系统中的应用
- 我不曾忘记的初心-10年
- 软件工程中的UML建模九图
- 提示“windows无法配置此无线连接,如果您已经起用其他程序管理此无线连接,请使用该软件.....”解决方法
- 【C】【笔记】《C语言深度剖析》第五章 内存管理
- 卷积膨胀 Dilation
- 【IEEE_Verilog-12.2】覆写模块参数的值
- java-net-php-python-jsp食堂刷卡系统计算机毕业设计程序
- 机器学习项目实战——预测学生是否被录取