本博文參考http://blog.csdn.net/v_july_v/article/details/7041827

关于其它字符串匹配算法见http://blog.csdn.net/WINCOL/article/details/4795369

暴力匹配算法

暴力匹配的思路。如果如今文本串S匹配到 i 位置,模式串P匹配到 j 位置。则有:

  • 假设当前字符匹配成功(即S[i] == P[j])。则i++,j++。继续匹配下一个字符;
  • 假设失配(即S[i]! = P[j]),令i = i - (j - 1),j = 0。相当于每次匹配失败时,i 回溯,j 被置为0。

KMP算法概述

KMP算法用于字符串匹配问题,核心思想是找到子串中的反复出现的连续字符并将其记录到数组中。通过这样的方式降低失配后回溯长度,以降低匹配次数。


KMP算法事实上是基于暴力匹配。并加上next数组之后的成果。


通俗易懂的解释见:点击打开链接(这是理解后面内容的基础)

KMP算法过程


如果如今文本串S匹配到 i 位置。模式串P匹配到 j 位置,则有:
  • 假设当前字符匹配成功(即S[i] == P[j])。则i++。j++。继续匹配下一个字符。
  • 假设失配(即S[i]! = P[j]),令i = i - (j - 1)。j = 0。相当于每次匹配失败时,j回溯到next[j]
能够发现:KMP算法和暴力匹配算法(BF算法)差别在于KMP算法中i不须要回溯且j回溯到next[j]的位置。

看过上面blog中通俗的解释之后,我们大概理解了KMP算法的核心思想,大概也能写出部分匹配值表。如今我们来搞懂next数组和部分匹配值表的关系。(此处搬来那篇博文中的这个表)

我们所用的移位公式为:

移动位数 = 已匹配的字符数 - 相应的部分匹配值

所以j每次回溯的移动位数就是已经匹配的字符数-相应匹配值,由于对于同一个字符串来
说,每一个字符的这些相关信息是固定的,我如今仅仅须要把这个信息放在一个数组里,每次
须要移动时候就直接让j等于相应位置的移动位数信息,这不就轻松加愉快了。因为每次都
通过这个数组j能够移动到它须要去的下一个位置,所以我们最好还是将其称为next组,每次执
行的过程就是是j=next[j]

那我们如今就要考虑怎样构建next数组。

那么问题就来了,我们移动的位数(即下次j将要去的地方)和当前的字符匹配值并没有什么
卵关系,它仅仅在乎已经匹配了的那个字符相关的信息。既然它不在乎我。那我还管它干嘛,
那么我们所须要的当前移动位数就和当前字符没啥关系了,是时候say
goodbye了。

可是尽管说当前匹配值和当前移动位数已经没什么关系了。它却影响了下一个位置的移动
位数,所以我们要将它和下一个位置关联起来。

如此一来。每一个当前位置的移动位数都与

前面的匹配值相关。而下一个位置的移动位数又与当前匹配值相关……我们自然而然就明确了
,我仅仅要将table表中的部分匹配值都右移一位,就能够得到next表了。


那么如今给我们一个字符串。我们就能够自己推出它的next数组了。

基本工作已经完毕了一

半,如今我们的任务就是通过代码的方式写出next数组的基本方法

基于之前的理解,我们能够明确next数组是能够通过递推求出的:

1.假设对于值k,已有p0 p1, ..., pk-1 = pj-k pj-k+1, ..., pj-1,相当于next[j]
= k。

即字符串開始的k-1个和当前位置j之前的k-1个相应相等。那么j下一次回溯的位置就是这个k
(由于前面部分都一致。再进行一遍就是无效回溯了)

2.依据已知的next[0...j]求next[j+1]

1)首先。若p[k]==p[j],则next[j+1]=next[j]+1=k+1;

这个非常好理解,就是仅仅要同样就+1即可了。

比方下图中C和C相等,所以next[j+1]=2+1=3;





2)若p[k]!=p[j],k索引next[k]直到与p[j]相等。此时可用公式next[j+1]=k+1(不能用
next[j+1]=next[j]+1。由于此时k已经变了,所以next[j]已经和k不相等了),若没有,则为0

理解起来就是:假设当前的字符不匹配,那么须要寻找长度更短的前缀后缀,让j回溯到
对应的位置(例如以下图)

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">



为何递归前缀索引k = next[k]。就能找到长度更短的同样前缀后缀呢?

这又归根到next数组的含义。我们拿前缀 p0 pk-1 pk 去跟后缀pj-k
pj-1 pj匹配,
假设pk 跟pj 失配,下一步就是用p[next[k]] 去跟pj 继续匹配,假设p[
next[k] ]跟pj
还是不匹配。则须要寻找长度更短的同样前缀后缀,即下一步用p[
next[ next[k] ] ]去跟pj匹

此过程相当于模式串的自我匹配,所以不断的递归k
= next[k],直到要么找到长度更短的同样前缀后缀。要么没有长度更短的同样前缀后缀。例如以下图所看到的:





如今我们来測试下k回溯是不是能够找到之前同样前后缀:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

因为此时的C和D不匹配,所以k走到next[k]即k=0,此时p[0]=p[j],所以next[j]=k+1=1。即字符E之前的字符串“DABCDABD”中有长度为1的同样前缀和后缀

此时我们最终大概明确了next数组的来历……也能自己敲出代码了,代码例如以下:

void GetNext(char* p,int next[])
{
int pLen = strlen(p);
next[0] = -1;
int k = -1;
int j = 0;
while (j < pLen - 1){
//p[k]表示前缀,p[j]表示后缀
if (k == -1 || p[j] == p[k]){
++k;
++j;
next[j] = k;
}
else k = next[k];
}
}

当然。此时的KMP算法还是基础版。还有优化版见:

http://baike.baidu.com/link?url=7TyIFAuf53azP6XofEc5oWivGEp5Gt9Dxnmhy5USg7eyiZzEBWiBVLjle1ZpSBUMU-Zqgeh9qqPiQwRdN4lqEq中的优化部分。

KMP算法——从入门到懵逼到了解的更多相关文章

  1. KMP算法-从入门到进阶

    题目描述 给定一个文本串text和模式串pattern,从文本串中找出模式串第一次出现的位置 先来看最简单的方法,方便理解题目,也就是暴力求解 暴力求解 放大上面的图,得到下面这个.题目要求匹配到整个 ...

  2. KMP算法之从懵逼到入门

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

  3. 【面向打野编程】——KMP算法入门

    一.问题 咱们先不管什么KMP,来看看怎么匹配两个字符串. 问题:给定两个字符串,求第二个字符串是否包含于第一个字符串中. 为了具体化,我们以 ABCAXABCABCABX 与 ABCABCABX为例 ...

  4. 【初识】KMP算法入门(转)

    感觉写的很好,尤其是底下的公式,易懂,链接:http://www.cnblogs.com/mypride/p/4950245.html 举个例子 模式串S:a s d a s d a s d f a  ...

  5. 【初识】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 ...

  6. KMP算法入门讲解

    字符串匹配问题.假设文本是一个长度为$n$的字符串$T$,模板是一个长度为$m$的字符串$P$,且$m\leq n$.需要求出模板在文本中的所有匹配点$i$,即满足$T[i]=P[0],T[I+1]= ...

  7. 萌新笔记——用KMP算法与Trie字典树实现屏蔽敏感词(UTF-8编码)

    前几天写好了字典,又刚好重温了KMP算法,恰逢遇到朋友吐槽最近被和谐的词越来越多了,于是突发奇想,想要自己实现一下敏感词屏蔽. 基本敏感词的屏蔽说起来很简单,只要把字符串中的敏感词替换成"* ...

  8. KMP算法的Next数组详解

    转载请注明来源,并包含相关链接. 网上有很多讲解KMP算法的博客,我就不浪费时间再写一份了.直接推荐一个当初我入门时看的博客吧:http://www.cnblogs.com/yjiyjige/p/32 ...

  9. 【转】KMP算法

    转载请注明来源,并包含相关链接.http://www.cnblogs.com/yjiyjige/p/3263858.html 网上有很多讲解KMP算法的博客,我就不浪费时间再写一份了.直接推荐一个当初 ...

随机推荐

  1. memcache的简单使用示例

    在实际应用中我们会缓存从数据库中查出来的结果集,以md5($sql)为$key,结果集为值. 以只是在php简单应用代码: <?php //建立memcache链接 $memcache = ne ...

  2. Strom的集群停止以及启动

    一:停止 1.概述 关于strom没有停止命令 2.第一种方式(kill) jps之后 使用bin/strom -kill wordcount 3.第二种方式(书写脚本) 4.先新建superviso ...

  3. 缓存击穿、缓存失效及热点key的解决方案

    分布式缓存是网站服务端经常用到的一种技术,在读多写少的业务场景中,通过使用缓存可以有效地支撑高并发的访问量,对后端的数据库等数据源做到很好地保护.现在市面上有很多分布式缓存,比如Redis.Memca ...

  4. Java中测试StringBuilder、StringBuffer、String在字符串拼接上的性能

    应一个大量字符串拼接的任务 测试一下StringBuilder.StringBuffer.String在操作字符串拼接时候的性能 性能上理论是StringBuilder  >  StringBu ...

  5. socket.io的用户认证

    一直专心于写代码,遇到问题便看别人的博客来解决问题,突然感觉自己也应该写点东西帮助别人来解决问题.废话不多说了,直接切入正题~ 最近在写一个聊天室来学习node和socket相关知识.遇到的问题描述: ...

  6. java8 Optional正确使用姿势

    Java 8 如何正确使用 Optional import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; ...

  7. Ubuntu crontab 定时 python 详细

    Ubuntu系统,定时执行python脚本. 目的:每分钟执行一次timer_test.py timer_test.py    路径 /home/li/d/pythonwork/test/timer_ ...

  8. Spring 注解@Transactional readOnly=true

    引子 今天下班后,以前同事小胖问我Spring  Service类中的注解@Transactional readOnly=true的作用.做为他眼中的高人,我自然要装下A-C.居然想都没有想就说是注解 ...

  9. Java并发程序设计(四)JDK并发包之同步控制

    JDK并发包之同步控制 一.重入锁 重入锁使用java.util.concurrent.locks.ReentrantLock来实现.示例代码如下: public class TryReentrant ...

  10. 潭州课堂25班:Ph201805201 爬虫高级 第一课 pyspider框架 (课堂笔记)

    利用wheel安装 S1: pip install wheelS2: 进入www.lfd.uci.edu/~gohlke/pythonlibs/,Ctrl + F查找pycurl 这个包名是pycur ...