目录

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算法相关推荐

  1. 【数据结构】详解KMP算法

    字符串匹配算法:简单来说就是给你一个主串和一个子串,让你查找子串在主串中的位置,找到返回下标. 常见的两种算法:BF算法.KMP算法 这两种算法是怎样的思路呢,我们接着往下看: 目录 BF算法(暴力算 ...

  2. 【数据结构-查找】2.字符串(逐步演绎过程,超级详解KMP算法)

    串的定义 串(string)是有0~n个字符组成的有限序列,一般记为 S=′a1a2-an′(n≥0)S = 'a_1a_2-a_n'(n≥0) S=′a1​a2​-an′​(n≥0) S 是字符串的 ...

  3. 详解KMP算法原理,以及完整java与C++实现

    点击此处学习更多算法与通信知识 作者 | labuladong 来源 | labuladong KMP 算法(Knuth-Morris-Pratt 算法)是一个著名的字符串匹配算法,效率很高,但是确实 ...

  4. 数据结构:详解KMP算法,手工求解next、nextval数组,求模式串的比较次数例题

    KMP 算法 手工求解 next 数组,nextval数组 例题:求模式串的比较次数 2019 年 408 统考真题 设主串 T="abaabaabcabaabc",模式串 S=& ...

  5. 5000字详解数据安全治理

    2010年,针对数据安全治理,微软提出了专门强调隐私.保密和合规的数据安全治理框架(DGPC),希望企业和组织能够以统一的跨学科的方式来实现目标,而非组织内不同部门独立实现.DGPC框架能够与企业现有 ...

  6. 一等奖,iPhone XR来了!A/B Test,个人中心首页改版实验分析报告(5000字详解)...

    作者:JM,从0到1规划过访客日浏览量超千万的流量分析系统,曾为年销售破百亿跨境电商公司商业分析系统的产品负责人,现为广告营销公司数据产品经理. 关键词:A/B Test 工具:友盟+移动统计(U-A ...

  7. 【算法知识】详解堆排序算法

    点击蓝色字关注我们! 什么是堆 「堆」首先是一个完全二叉树,「堆」分为「大顶堆」和「小顶堆」: 「大顶堆」 : 每个节点的值大于或等于其左右孩子节点的值,称为大顶堆. 「小顶堆」同理就是每个节点的值小 ...

  8. 详解rsync算法--如何减少同步文件时的网络传输量

    详解rsync算法--如何减少同步文件时的网络传输量 先看下图中的场景,客户端A和B,以及服务器server都保存了同一个文件,最初,A.B和server上的文件内容都是相同的(记为File.1).某 ...

  9. 【算法知识】详解基数排序算法

    已发布: [算法知识]详解选择冒泡算法 [算法知识]详解选择排序算法 [算法知识]详解插入排序算法 [算法知识]详解快速排序算法 [算法知识]详解归并排序算法 基本思想 基数排序的思想是将整数按位数切 ...

最新文章

  1. linux 删除mysql
  2. 新网 云服务器,新网云服务器的优势包括什么?
  3. 弹出页(指定高度,自由拖动,点击空白包括状态栏触发)
  4. sm4加密 解密(oc)
  5. 上海事业编制 计算机 待遇怎么样,事业单位情况
  6. MyBatis源码解析(十二)——binding绑定模块之MapperRegisty
  7. Linux 内存中的Cache
  8. 【转】数据库范式(1NF 2NF 3NF BCNF)详解二
  9. ArcGIS pro 发布地图服务(一)动态地图服务
  10. 三端稳压管型号选型对照大全-KIA半导体
  11. 用python获取实时地球图像作为壁纸(windows)
  12. 自适应音频功率放大器
  13. android 如何进入安全模式,手机怎么进入安全模式
  14. 第一章 引论 -- 项目管理知识体系指南(PMBOK指南)(第五版)
  15. HTTP协议和URLConnection使用
  16. 什么是SQL注入?怎么解决SQL注入?
  17. ftp服务器修改pasv,ftp服务器修改pasv
  18. 计蒜客 17115 Coin(2017 ACM-ICPC 亚洲区(西安赛区)网络赛 B)
  19. 服务器性能测试综合实验报告,CPU性能测试实验报告.doc
  20. c一些基本算法和公式

热门文章

  1. java web乱码及解决方法
  2. uIP tcp/ip协议分析及其在嵌入式系统中的应用
  3. 我不曾忘记的初心-10年
  4. 软件工程中的UML建模九图
  5. 提示“windows无法配置此无线连接,如果您已经起用其他程序管理此无线连接,请使用该软件.....”解决方法
  6. 【C】【笔记】《C语言深度剖析》第五章 内存管理
  7. 卷积膨胀 Dilation
  8. 【IEEE_Verilog-12.2】覆写模块参数的值
  9. java-net-php-python-jsp食堂刷卡系统计算机毕业设计程序
  10. 机器学习项目实战——预测学生是否被录取