讲KMP算法,离不开BF,实际上,KMP就是BF升级版,主要流程和BF一样

不同是在匹配失败时能利用子串的特征减少回溯,利用根据子串特征生成的Next数组来减少

<( ̄︶ ̄)↗[GO!]

!!!所有数组下标都是从0开始

1. 先看看BF算法(暴力破解)

int Brute_force_1(const char *S, const char *T)
{
if (!S || !T)
return -1;
int lenS = strlen(S);
int lenT = strlen(T);
int i = 0; //主串下标索引
int j = 0; //子串下标索引
while(i < lenS && j < lenT)
{
if (S[i] == T[j]) //如果相等一直继续往下匹配
++i,++j;
else //不相等i和j开始回溯
{
i = i-j+1;
j = 0;
}
}
if (j == lenT)
return i - j;
return -1;
}

​ BF算法有几种不同实现,但最终思想都是一样的,以下就是另一个BF实现

int Brute_force_2(const char *S, const char *T)
{
if (!S || !T)
return -1;
int lenS = strlen(S);
int lenT = strlen(T);
for (int i = 0; i <= lenS - lenT; ++i)
{
int k = i, j = 0;
while (k < lenS && j < lenT && S[k] == T[j])
{
++j;
++k;
}
if (j == lenT)
return i; //说明匹配到了
}
return -1;
}

​ 你完全可以根据自己的理解写出BF算法,但在这里,为了BF和KMP统一,我们还是采用第一种实现,即容易看出回溯操作的实现

2. Next[]数组

​ 事实上,书上的next数组生成算法是经过优化后的算法,比较难懂,但你完全可以按照自己的理解做一个

注意:Next[]数组只是在KMP中字符串匹配失败时使用的

void GetNext(int Next[], char *str)
{
assert(str!=NULL);
int len = strlen(str);
if(len>1)Next[0]=0;
//其实Next[0]等于0或者等于-1效果没什么影响,
//因为在KMP中不匹配时判断是不是第一个字符不匹配用用的是j==0;-----if (j==0||Next[j]==0),
if(len>2)Next[1]=0;
//Next[]等于0时说明需要讲i回溯到子串头的下一个位置(i=i-j+1);
//此时j也回到子串头位置(j=0)
for(int i=2;i<len;++i)
{
for(int j=i-1;j>0;--j)
{
if(!strncmp(&str[0],&str[i-j],j))
{
Next[i]=j;break; //找到最大重复子子串(子串中的子串)
//Next[]为其他值则i不变,讲j回溯到Next[j]的位置(j=Next[j])
}
else Next[i]=0;
}
}
}

​ 这个时间复杂度要比书上的方法高很多,但好理解,真实的反映了Next数组的本质。

3. KMP

int KMP(const char *S, const char *T, const int *Next)
{
if (!S || !T||!Next)
return -1;
int lenS = strlen(S);
int lenT = strlen(T);
int i = 0; //主串下标索引
int j = 0; //子串下标索引
while(i < lenS && j < lenT)
{
if (S[i] == T[j]) ++i,++j; //若相等则继续匹配下一个字符
else //不相等则回溯
{
//(当j==0时,即第一个字符不匹配,和Next[j]==0时事实上与BF算法相同)
if (j==0||Next[j]==0)
{
i = i-j+1;
j = 0;
}
else j = Next[j];//主串i位置不变,讲子串下标索引挪到Next[j]的位置
}
}
if (j == lenT)
return i - j;
return -1;
}

​ 这个回溯时的操作实际上是把两种情况合成一种,拆开后就是下面的,就是生成next数组那块三种情况

while (i < lenS && j < lenT)
{
if (S[i] == T[j])
++i, ++j;
else
{
if (j == 0)
{
++i; //等价于i = i-0+1;j本身就等于0
}
else if (Next[j] == 0)
{
i = i - j + 1;
j = 0;
}
else
{
j = Next[j];
}
}
}

扩展

​ Next数组有进一步改进的可能,如果发生失配,失配点子串字符若与回溯到的字符相同,则再次匹配肯定失败,所以改进的Next数组进一步处理了这种情况,消除了回溯

void GetNext_pro(int Next[], const char *str)
{
assert(str!=NULL);
int len = strlen(str);
if(len>1)Next[0]=-1;
//其实Next[0]等于0或者等于-1效果没什么影响,
//因为在KMP中不匹配时判断是不是第一个字符不匹配用用的是j==0;-----if (j==0||Next[j]==0),
if(len>2)Next[1]=0;
//Next[]等于0时说明需要讲i回溯到子串头的下一个位置(i=i-j+1);
//此时j也回到子串头位置(j=0)
for(int i=2;i<len;++i)
{
for(int j=i-1;j>0;--j)
{
if(!strncmp(&str[0],&str[i-j],j))
{
if(str[i]==str[j])
Next[i]==Next[j];
else
Next[i]=j;
break; //找到最大重复子子串(子串中的子串)
//Next[]为其他值则i不变,讲j回溯到Next[j]的位置(j=Next[j])
}
else Next[i]=0;
}
}
}

测试代码

int KMP(const char *S, const char *T)
{
if (!S || !T)
return -1;
int Next[MAXSIZE] = {0};
GetNext(Next,T);
print_arr(Next, strlen(T));
GetNext_pro(Next,T);
print_arr(Next, strlen(T)); int lenS = strlen(S);
int lenT = strlen(T);
int i = 0; //主串下标索引
int j = 0; //子串下标索引
while(i < lenS && j < lenT)
{
if (S[i] == T[j])
++i,++j; //若相等则继续匹配下一个字符
else //不相等则回溯
{
//(当j==0时,即第一个字符不匹配,和Next[j]==0时事实上与BF算法相同)
if (j==0||Next[j]==0)
{
i = i-j+1;
j = 0;
}
else j = Next[j];//主串i位置不变,将子串下标索引挪到Next[j]的位置
}
}
if (j == lenT)
return i - j;
return -1;
}
int main(void)
{
char source[MAXSIZE] = "adcfabadcf";
char target[MAXSIZE] = "abcabcabbac";
printf("%d\n", Brute_force_1(source, target));
printf("%d\n", Brute_force_2(source, target));
printf("%d\n", KMP(source, target)); getchar();
return 0;
}
附上BF与KMP的比较,你会发现两者其实挺相似

总结

​ 其实核心就在于本文第一句话的理解。

​ KMP在子串含有相同前后缀时,利用Next数组减少匹配失败时的回溯次数有优势,而改进的Next数组在此基础上若子串含有较多相同字符则更进一步减少回溯。

​ 所以KMP总之是利用子串的特征来削除回溯,如果子串并不具有这些特征,那就还没有BF好,因为KMP还需要额外的空间来存放Next数组

​ 书上的next数组的生成很难懂,加油理解中。。。(ง •_•)ง

KMP 算法简单解释的更多相关文章

  1. KMP算法具体解释(贴链接)

    ---------------------------------------------------------------------------------------------------- ...

  2. KMP算法具体解释(转)

    作者:July. 出处:http://blog.csdn.net/v_JULY_v/. 引记 此前一天,一位MS的朋友邀我一起去与他讨论高速排序,红黑树,字典树,B树.后缀树,包含KMP算法,只有在解 ...

  3. KMP算法简单回顾

    前言 虽从事企业应用的设计与开发,闲暇之时,还是偶尔涉猎数学和算法的东西,本篇根据个人角度来写一点关于KMP串匹配的东西,一方面向伟人致敬,另一方面也是练练手,头脑风暴.我在自娱自乐,路过的朋友别太认 ...

  4. KMP算法具体解释

    这几天学习kmp算法,解决字符串的匹配问题.開始的时候都是用到BF算法,(BF(Brute Force)算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配 ...

  5. 串的应用与kmp算法讲解--学习笔记

    串的应用与kmp算法讲解 1. 写作目的 平时学习总结的学习笔记,方便自己理解加深印象.同时希望可以帮到正在学习这方面知识的同学,可以相互学习.新手上路请多关照,如果问题还请不吝赐教. 2. 串的逻辑 ...

  6. 字符串匹配KMP算法的C语言实现

    字符串匹配是计算机的基本任务之一. 举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串"ABCDABD" ...

  7. 字符串匹配(KMP 算法 含代码)

    主要是针对字符串的匹配算法进行解说 有关字符串的基本知识 传统的串匹配法 模式匹配的一种改进算法KMP算法 网上一比較易懂的解说 小样例 1计算next 2计算nextval 代码 有关字符串的基本知 ...

  8. (收藏)KMP算法的前缀next数组最通俗的解释

    我们在一个母字符串中查找一个子字符串有很多方法.KMP是一种最常见的改进算法,它可以在匹配过程中失配的情况下,有效地多往后面跳几个字符,加快匹配速度. 当然我们可以看到这个算法针对的是子串有对称属性, ...

  9. KMP算法的next[]数组通俗解释

    原文:https://blog.csdn.net/yearn520/article/details/6729426 我们在一个母字符串中查找一个子字符串有很多方法.KMP是一种最常见的改进算法,它可以 ...

随机推荐

  1. 网购分期不还 N种恶果等着你

    N种恶果等着你" title="网购分期不还 N种恶果等着你"> 网购市场狂飙突进的发展,让每个人都享受到随时随地购物的乐趣,也在很大程度上推动商品之间的流通.目前 ...

  2. 基于mykernel完成时间片轮询多道进程的简单内核

    基于mykernel完成时间片轮询多道进程的简单内核 原创作品转载请注明出处+中科大孟宁老师的linux操作系统分析:https://github.com/mengning/linuxkernel/ ...

  3. IIS+PHP+Mysql 返回500,服务器内部资源问题

    这个错误困扰了我好久.... 尝试了好多方法都不管用,最后突然发现我的代码是: <?php $link=mysql_connect("localhost","xxx ...

  4. 达拉草201771010105《面向对象程序设计(java)》第十周学习总结

    达拉草201771010105<面向对象程序设计(java)>第十周学习总结 实验十  泛型程序设计技术 实验时间 2018-11-1 第一部分:理论知识        泛型:也称参数化类 ...

  5. Ajax&Json案例

    案例: * 校验用户名是否存在 1. 服务器响应的数据,在客户端使用时,要想当做json数据格式使用.有两种解决方案: 1. $.get(type):将最后一个参数type指定为"json& ...

  6. C#开发BIMFACE系列30 服务端API之模型对比1:发起模型对比

    系列目录     [已更新最新开发文章,点击查看详细] 在实际项目中,由于需求变更经常需要对模型文件进行修改.为了便于用户了解模型在修改前后发生的变化,BIMFACE提供了模型在线对比功能,可以利用在 ...

  7. 攻防世界Mobile5 EasyJNI 安卓逆向CTF

    EasyJNI 最近正好在出写JNI,正好看到了一道JNI相关的较为简单明了的CTF,就一时兴起的写了,不得不说逆向工程和正向开发确实是可以互补互相加深的 JNI JNI(Java Native In ...

  8. 学习经典算法—JavaScript篇(一)排序算法

    前端攻城狮--学习常用的排序算法 一.冒泡排序 优点: 所有排序中最简单的,易于理解: 缺点: 时间复杂度O(n^2),平均来说是最差的一种排序方式: 因为在默认情况下,对于已经排好序的部分,此排序任 ...

  9. HDFS DataNode详解

    1. datanode介绍 1.1 datanode datanode是负责当前节点上的数据的管理,具体目录内容是在初始阶段自动创建的,保存的文件夹位置由配置选项{dfs.data.dir}决定 1. ...

  10. JavaScript(6)--- 原型链

    原型链 再上一篇有简单讲过原型:JavaScript(5)--- 面向对象 + 原型 讲原型链知识之前,先说几个重要的结论. 1.原型链就是 对象的__proto__所连接的链状结构 2.protot ...