KMP算法-从入门到进阶
题目描述
给定一个文本串text和模式串pattern,从文本串中找出模式串第一次出现的位置

先来看最简单的方法,方便理解题目,也就是暴力求解
暴力求解
放大上面的图,得到下面这个。题目要求匹配到整个字符串,从开始匹配考虑。

用模式串的首元素去匹配文本串的每一个元素,如果能匹配到,则依次向后匹配,直到所有的模式串匹配成功。
如果模式串中有一个不匹配,则pattern回到首元素匹配test中的下一元素。
这里需要注意的是,模式串首元素需要匹配的最后一个元素是text-j,因为如果匹配到最后,模式串比text长是没有意义的。
整个流程,可以想象是先把模式串与text对齐,然后相对于text依次后移一位,拖动pattern,每次移动都比较整个pattern模式串每个元素(理解这个有助于后面分析)
关键代码如下
int search(const char*s, const char*p)
{
int i = 0;//用于标记匹配到text字符串的位置
int j = 0;//标记模式串中匹配的位置
int size = (int)strlen(p);
int nLast = (int)strlen(s) - size; //此处为匹配到text中最长位置
while((i <= nLast) && (j < size))
{
if (s[i+j] == p[j])
{
j ++;
}
else{
i++;
j = 0; //j回到模式串首元素
}
}
if(j >= size)
return i;
return -1;
}
记text长度为N,pattern长度为M。在这个方法中,时间复杂程度为O(M*N),空间复杂程度为O(1)
进一步分析:
在暴力求解中,为什么模式串的索引 j 会回溯?原因是模式串需要依次匹配整个才能知道是否完全匹配。
所以,增加一个条件,如果模式串的字符两两不相等。也就意味着模式串只要一次不匹配,整个 j 的长度都不会匹配上,就可以向后拖动pattern到自身长度的位置。
即,如果发生了不匹配,则向后移动 i+j 个位置开始匹配。
现在整个算法的时间复杂度退化成了O(N),但这是在模式串两两不等的情况下才有的结论。那这个条件可以弱化吗?
当然是可以,弱化后条件变为:模式串首字符和其他字符不相等。

继续分析:
在上图中P和D是刚好不匹配的一个元素。为了让pattern能尽可能多的往后拖动,那么拖动到一个什么样的位置是合适的呢?
如果说拖动到一个C与P开始比较的位置,那么意味着A==Q。因为只有事先知道Q==A,才能进行P与C的比较。这次拖动的上一步中P!=D匹配失败,B也刚与Q匹配完,B==Q。所以由此推出B和A是相等的。
通过上面的分析,已经得到了一个拖动模式串的规则。下面继续分析如何提前找到A==B
求解next数组
定义:如上字符串中,A为D的前缀串,B为D的后缀串。A==B且是最长串,这个最长串组成的数据即为next数组。下面列表举例:
| pattern | a | b | a | a | b | c | a | b | a |
| next | -1 | 0 | 0 | 1 | 1 | 2 | 0 | 1 | 2 |
pattern的每个元素的next值,代表它前面的字符串,前缀后缀最长匹配长度。这个next数组表示就是pattern最远能滑动到的位置
那么如何求这个数组呢?下面分析next数组的递推关系:

next[j]=k,则对于模式串的位置j+1,考察Pj:
若p[k]=p[j]:则next[j+1]=next[j]+1
这个好理解,j+1的最长匹配的前后串就是在原来的基础上增加了一个 k==j 元素
若p[k]!=p[j]:则记h=next[k];如果p[h]==p[j],则next[j+1]=h+1,否则重复此过程
对于元素k来说,next[k]是k前面的字符串最长匹配长度,同时B和A是已经匹配的串。那么P==Q==X==Y。p[h]是P字段后面的一个元素,如果p[h]==p[j],就是P字段加上p[h]与Y字段加上p[j]匹配。故next(j+1)=h+1
参考代码片段如下:
void GetNext(char *p, int next[])
{
int nLen = (int)strlen(p);
next[0] = -1;
int k = -1;
int j = 0;
while (j < nLen - 1)
{
//这里,k表示next[j-1],且p[k]表示前缀,p[j]表示后缀
//注:k==-1表示未找到k前缀与k后缀相等,首次分析可先忽略
if (k == -1 || p[j] = p[k])
{
++j;
++k;
next[j] = k; //已经做了++j计算,此处不写j+1
}else{ //p[j]与p[k]不匹配,继续递归计算前缀p[next[k]]
k = next[k];
}
}
}
int KMP(int n)
{
int ans = -1;
int i = 0;
int j = 0;
int pattern_len = strlen(g_pattern);
while(i < n)
{
if(j == -1 || g_s[i] == g_pattern[j])
{
++i; ++j;
}else
j = g_next[j]
if(j == pattern_len)
{
ans = i - pattern_len;
break;
}
}
return ans;
}
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 ...
- 算法进阶面试题01——KMP算法详解、输出含两次原子串的最短串、判断T1是否包含T2子树、Manacher算法详解、使字符串成为最短回文串
1.KMP算法详解与应用 子序列:可以连续可以不连续. 子数组/串:要连续 暴力方法:逐个位置比对. KMP:让前面的,指导后面. 概念建设: d的最长前缀与最长后缀的匹配长度为3.(前缀不能到最后一 ...
- KMP算法之从懵逼到入门
写本文的目的: 1.加深自己的理解,以便自己日后复习 2.给看到此文的人一点启发 KMP算法看懂了就觉得特别简单,思路也好理解,但是看不懂之前,查各种资料看大佬的博客,都很懵逼...... 1. 算 ...
- 【初识】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算法
KMP算法是字符串模式匹配当中最经典的算法,原来大二学数据结构的有讲,但是当时只是记住了原理,但不知道代码实现,今天终于是完成了KMP的代码实现.原理KMP的原理其实很简单,给定一个字符串和一个模式串 ...
- 萌新笔记——用KMP算法与Trie字典树实现屏蔽敏感词(UTF-8编码)
前几天写好了字典,又刚好重温了KMP算法,恰逢遇到朋友吐槽最近被和谐的词越来越多了,于是突发奇想,想要自己实现一下敏感词屏蔽. 基本敏感词的屏蔽说起来很简单,只要把字符串中的敏感词替换成"* ...
随机推荐
- MySQL后记
MySQL后记 这篇博客的目的是记录一些容易被忽略的MySQL的知识点,以及部分pymysql模块的注意点. MySQL中的DDL与DML DDL:数据定义语言(Data Definition Lan ...
- Viper解析&加载配置
Viper解析&加载配置 1 Viper是什么 Viper是一个方便Go语言应用程序处理配置信息的库.它可以处理多种格式的配置.它支持的特性: 设置默认值 从JSON.TOML.YAML ...
- Gradle系列之构建脚本基础
原文发于微信公众号 jzman-blog,欢迎关注交流. 前面两篇文章分别介绍了 Gradle 基础知识以及 Groovy 相关基础知识,这也是学习 Gradle 所必需了解的,文章链接如下:: Gr ...
- 【高性能Mysql 】读书笔记(三)
第5章 创建高性能的索引 本文为<高性能Mysql 第三版>第四章读书笔记,Mysql版本为5.5 索引基础 索引的重要性:找一本800面的书的某一段内容,没有目录也没有页码(页码也可类比 ...
- Burp Suite Repeater Module - 中继模块
目的:节省网页应用分析时间 目标对象:http://10.0.0.15/getboo/login.php 通过调整Request的参数,不断尝试,通过Response查看状态.从而节省在浏览器中操作的 ...
- Eclipse点击空格总是自动补全代码怎么办,如何自动补全代码,代码提示
Eclipse点击空格总是自动补全不想要的代码说明大家配置的时候出现了一点错误,下面的步骤将会解决它, 网上部分经验需要大家更改代码非常繁琐,下面是一个简单的步骤方法 步骤一:打开eclipse依次点 ...
- vue邪道玩法 : 把vue实例存在别的地方,以及可能会遇到的问题
一般来说,VUE项目中,this指向VUE实例. 但有的时候,某些代码会改变this的指向. 这时,可以用一个临时变量存储VUE实例. test1(){ var _this = this // 把vu ...
- vue : 对 vue-class-component 的个人理解
vue-class-component 是 vue 的官方库,作用是用类的方式编写组件. 这种编写方式可以让.vue文件的js域结构更扁平,并使vue组件可以使用继承.混入等高级特性. 简单的示例: ...
- 从0搭建一个基于 ELK 的日志、指标收集与监控系统
为了使得私有化部署的系统能更健壮,同时不增加额外的部署运维工作量,本文提出了一种基于 ELK 的开箱即用的日志和指标收集方案. 在当前的项目中,我们已经使用了 Elasticsearch 作为业务的数 ...
- .Net、ASP.Net、C#、VisualStudio之间的关系是什么
.Net一般指的是.NetFramework,提供了基础的.Net类,这些类可以被任何一种.Net编程语言调 用,.NetFramework还提供了 CLR.JIT.GC等基础功能. ASP.Net是 ...