KMP算法之从懵逼到入门
写本文的目的:
1.加深自己的理解,以便自己日后复习
2.给看到此文的人一点启发
KMP算法看懂了就觉得特别简单,思路也好理解,但是看不懂之前,查各种资料看大佬的博客,都很懵逼......
1. 算法过程解释


首先,字符串"BBCABCDAB ABCDABCDABDE"的第一个字符与搜索词"ABCDABD"的第一个字符,进行比较。因为B与A不匹配,所以搜索词后移一位。
就这样,直到字符串有一个字符,与搜索词的第一个字符相同为止。

接着比较字符串和搜索词的下一个字符,还是相同。
直到字符串有一个字符,与搜索词对应的字符不相同为止。

这时,最自然的反应是,将搜索词整体后移一位,即从上图B处再从头逐个比较。这样做虽然可行,但是效率很差,因为你要把搜索的初始位置移到已经比较过的位置,重比一遍。

一个基本事实是,当空格与D不匹配时,你其实知道前面六个字符是"ABCDAB"。KMP算法的想法是,此时不只移动一位,移动数是已经比较的字符数 - 最后一个匹配字符所对应的部分匹配值,这个部分匹配值实质上就是字符串头部和尾部重复部分的最大长度。因此就有了部分匹配值数组:


已知空格与D不匹配时,前面六个字符"ABCDAB"是匹配的。查表可知,最后一个匹配字符B对应的"部分匹配值"为2,因此向后移动的位数为已匹配的字符数减去对应的部分匹配值,即6-2=4。

因为空格与C不匹配,搜索词还要继续往后移。这时已匹配的字符数为2("AB"),最后一个匹配字符B对应的"部分匹配值"为0。所以,移动位数为 2,于是将搜索词向后移2位。
因为空格与A不匹配,继续后移一位。

逐位比较,直到发现C与D不匹配。于是,移动位数为 6 - 2,继续将搜索词向后移动4位。

逐位比较,直到搜索词的最后一位,发现完全匹配,于是搜索完成。如果需要找出全部的匹配,移动位数为7 - 0,再将搜索词向后移动7位,剩下的操作就重复了。
首先,要了解两个概念:前缀和后缀。
"前缀"指除了最后一个字符以外,一个字符串的全部头部组合;
"后缀"指除了第一个字符以外,一个字符串的全部尾部组合。
"部分匹配值"就是"前缀"和"后缀"的最长的共有元素的长度。以"ABCDABD"为例:

- "A"的前缀和后缀都为空集,共有元素的长度为0;
- "AB"的前缀为[A],后缀为[B],共有元素的长度为0;
- "ABC"的前缀为[A, AB],后缀为[BC, C],共有元素的长度0;
- "ABCD"的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0;
- "ABCDA"的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为"A",长度为1;
- "ABCDAB"的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为"AB",长度为2;
- "ABCDABD"的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。
KMP算法的核心思想(个人理解):根据子串确定每次匹配失败的时候主串开始比较位向前移动的位数,位数=已经比较的字符数 - 最后一个匹配字符所对应的部分匹配值,这个就是KMP和暴力匹配算法的根本区别
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
void getnext(char a[],int l,int next[])
{
//a字符串数组为子串,l为字符串a的长度,next为a的匹配值数组
int j;
int k=;
next[]=;//初始化
j=;
while(j<=l-)
{
if(k==)//a[0]和a[x]比较
{
if(a[k]==a[j])
{ k++;//k向后移动一位
next[j]=k;
j++;
}else
{
//k不动
next[j]=k;
j++;
}
}
if(k!=)//k此时不在a[0]的位置上
{
if(a[k]==a[j])
{
k++;//k后移一位
next[j]=k;
j++;//j后移一位
}
else
{
k=;//k重新回到a[0]
}
}
}
}
void KMP(char str[],char a[])
{
int L=strlen(str);//字符串长度
int l=strlen(a);
int i,j;
i=j=;
int next[l];
getnext(a,l,next);//活动匹配值数组
int sum=;//匹配成功的次数
while(i<=L&&j<=l)
{
if(str[i]==a[j]&&j==)//匹配中的四种情况
{
i++;
j++;
}else if(str[i]==a[j]&&j!=)
{
i++;
j++;
}else if(str[i]!=a[j]&&j==)
{
j=;
i++;
}else if(str[i]!=a[j]&&j!=)
{
int s=j-next[j-];
i=i-j+s;
j=;
}
if(j==l)//匹配成功的条件
{
printf("第%d此成功匹配的位置为:%d\n",sum,i-l);
sum++;
}
} }
int main()
{
char str[],a[];
gets(str);
gets(a);
KMP(str,a);
return ;
}
参考:
http://blog.csdn.net/seu_calvin/article/details/62232825
http://blog.csdn.net/starstar1992/article/details/54913261
不足错误之处欢迎拍砖!!!!!
KMP算法之从懵逼到入门的更多相关文章
- KMP算法——从入门到懵逼到了解
本博文參考http://blog.csdn.net/v_july_v/article/details/7041827 关于其它字符串匹配算法见http://blog.csdn.net/WINCOL/a ...
- 【面向打野编程】——KMP算法入门
一.问题 咱们先不管什么KMP,来看看怎么匹配两个字符串. 问题:给定两个字符串,求第二个字符串是否包含于第一个字符串中. 为了具体化,我们以 ABCAXABCABCABX 与 ABCABCABX为例 ...
- 【初识】KMP算法入门(转)
感觉写的很好,尤其是底下的公式,易懂,链接:http://www.cnblogs.com/mypride/p/4950245.html 举个例子 模式串S:a s d a s d a s d f a ...
- 【初识】KMP算法入门
举个例子 模式串S:a s d a s d a s d f a s d 匹配串T:a s d a s d f 如果使用朴素匹配算法—— 1 2 3 4 5 6 8 9 a s d a s d a s ...
- KMP算法入门讲解
字符串匹配问题.假设文本是一个长度为$n$的字符串$T$,模板是一个长度为$m$的字符串$P$,且$m\leq n$.需要求出模板在文本中的所有匹配点$i$,即满足$T[i]=P[0],T[I+1]= ...
- 萌新笔记——用KMP算法与Trie字典树实现屏蔽敏感词(UTF-8编码)
前几天写好了字典,又刚好重温了KMP算法,恰逢遇到朋友吐槽最近被和谐的词越来越多了,于是突发奇想,想要自己实现一下敏感词屏蔽. 基本敏感词的屏蔽说起来很简单,只要把字符串中的敏感词替换成"* ...
- KMP算法的Next数组详解
转载请注明来源,并包含相关链接. 网上有很多讲解KMP算法的博客,我就不浪费时间再写一份了.直接推荐一个当初我入门时看的博客吧:http://www.cnblogs.com/yjiyjige/p/32 ...
- 【转】KMP算法
转载请注明来源,并包含相关链接.http://www.cnblogs.com/yjiyjige/p/3263858.html 网上有很多讲解KMP算法的博客,我就不浪费时间再写一份了.直接推荐一个当初 ...
- KMP算法的Next数组详解 转
这个写的很好,还有讲kmp,值得一看. http://www.cnblogs.com/tangzhengyue/p/4315393.html 转载请注明来源,并包含相关链接. 网上有很多讲解KMP算法 ...
随机推荐
- 用CSS的方法如何让一个元素不可见?(面试题目)
面试中看到这个问题,自己想的不全面,下面整理下,一起学习: 一.CSS元素隐藏 在CSS中,让元素隐藏(指屏幕范围内肉眼不可见)的方法很多,有的占据空间,有的不占据空间:有的可以响应点击,有的不能响应 ...
- CSS 样式初始化
去除浏览器对html的附加样式,避免不同浏览器之间的样式差异,给前端开发提供统一的样式基础.附加样式: .clearfix - 清除浮动 .wordsBreak - 允许文本在任意位置的换行 .ell ...
- windows 7 Alt+Tab 的风格改成 XP 风格
1.开始菜单-运行-输入“regedit”. 2.找到这个位置“[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explore ...
- SpringBoot 之HelloController
1.搭建环境 1.1 新建一个maven项目,一路下一步 1.2 打开pom 文件,首先增加<parent></parent>标签: <parent> <gr ...
- 使用docker安装使用gitlab
1.下载镜像 gitlab/gitlab-ce:latest 当前gitlab最新版本为10.0.4 2.在服务器上创建目录 mkdir -p /home/work/ins/co ...
- 浅谈Java——泛型DAO
首先解释一下为什么要学习泛型DAO.平时在写DAO的时候是一个接口对应一个实现类,实现类里面要写很多的操作数据库的方法.当我们有很多的javaben的时候我们会写很多的接口和实现类,并且里面的代码都是 ...
- 细说C#继承
简介 继承(封装.多态)是面向对象编程三大特性之一,继承的思想就是摈弃代码的冗余,实现更好的重用性. 继承从字面上理解,无外乎让人想到某人继承某人的某些东西,一个给一个拿.这个语义在生活中,就像 家族 ...
- C#.NET里面抽象类,接口,虚方法
1抽象类 (1) 抽象方法只作声明,而不包含实现,可以看成是没有实现体的虚方法 (2) 抽象类不能被实例化 (3) 抽象类可以但不是必须有抽象属性和抽象方法,但是一旦有了抽象方法,就一定要把这个类声明 ...
- [翻译] AFSoundManager
AFSoundManager iOS audio playing (both local and streaming) and recording made easy through a comple ...
- Office 365实现单点登录系列(2)—Azure AD Connect安装与配置
前言 第一篇文章我已经为大家分享了在在Azure上搭建域控服务器的方法,如果大家本地已经有了域环境,可以直接从这一篇文章开始阅读.Azure AD Connect的前身是DirSync,是专门用于目录 ...