kmp算法详解

时间:2022-08-25 14:59:38

转自:http://blog.csdn.net/ddupd/article/details/19899263

KMP算法详解

KMP算法简介:

KMP算法是一种高效的字符串匹配算法,关于字符串匹配最简单的就是BF算法。BF算法是用两个游标分别指向母串S,模式串T,从开头向后面依次比较字符是否相等,如果相等继续同时向后滑动两个游标,不相等的话,T的游标回溯至开头,S的游标回溯至起初游标的下一位,这种算法原理非常简单,小学生都可以想的到。

KMP算法是在BF算法的基础上加以改进的,它的特点是在遇到字符不匹配时候维持母串T的游标不动,而把模式串向右移动,具体移动到哪一个元素下标,这就是算法的核心思想之处了。

假如母串的i处和模式串的j处不匹配,那么就令k=next(j),表示的意思就是:模式串在j处出现不匹配现象,此时应该将模式串向后一定到下标为k的游标处,在此与之前不匹配的元素进行比较。

Kmp算法的本质:

如图所示:

kmp算法详解

在下标j处出现不匹配,则k = next(j),表示此时应该把下标k移动到原本j对应的位置处,用T[k]跟s[i]进行对比。如果满足这样的条件,则有T[0],T[1],…T[k-1] = S[i-k],S[i-k+1],…S[i-1]

又因为j之前的字符串跟S都匹配,所以又有T[j-k],T[j-k+1],…T[j-1] = S[i-k],S[i-k+1],…S[i-1].所以得出  T[0],T[1],…T[k-1] = T[j-k],T[j-k+1],…T[j-1]。也就是说图中被标记出来前后两个区域的字符串相等,KMP算法的本质就是找出最大的这样一个k值满足T[0],T[1],…T[k-1] = T[j-k],T[j-k+1],…T[j-1]。

K值的求取方法:

K值的求取用到了数学中的递推的思想,求取K值只跟模式串T自身有关,跟母串S半毛钱关系都没有。先假设已经有 next(j) = k,接下来我们就去求next(j+1)的值。这个要分情况讨论:

如果T[k] = T[j]那么就很容易得到 next(j+1) = k+1 = next(j) + 1;

如果T[k] != T[j],那么此时可以将T[0],T[1],…T[k-1],T[k]看做一个模式串,T[j-k],T[j-k+1],…T[j-1],T[j]看做一个母串,此时模式串在k处出现不匹配现象,那么我们获取next(k)= k1的值,判断T[k1]跟T[j]的值是否相等,如果相等那么next(j+1) = k1+1;如果不相等再往下求新的kn的值,直到T[kn]= T[j],或者遍历到了模式串的开头都不想的话,此时就要把i向后移动一个位置,同时用模式串的开头指向i,或者抽象一点就是把模式串开头的前一位下标(-1)指向i。因为下标(-1)是没有意义的,所以此时等效于下标(0)指向母串的i+1。

算法的实现:

这里一共列出了三个版本的kmp算法,其中第一个是本人根据对算法的理解写的,也是最丑的一个,剩下的两个是改编严蔚敏版的《数据结构与算法》一书中的。

 
 //Algorithms.cpp
#include "Algorithms.h"
#include <vector>
#include <iostream>
using namespace std; Algorithms::Algorithms(void)
{
}
Algorithms::~Algorithms(void)
{
} int Algorithms::kmp1(string strS,string strT)
{
int nSize = strT.size();
vector<int> vecNext(nSize,-);
if (nSize >= )
vecNext[] =;
for (int i=;i<nSize;i++)
{
int k = vecNext[i-];
while (k>= && strT[k] != strT[i-] )
k = vecNext[k];
if(k>= && strT[i-] == strT[k])
vecNext[i] = k + ;
else
vecNext[i] = ;
}
for(int i=;i<nSize;i++)
cout<<"the vector is:"<<i<<": "<<vecNext[i]<<endl; int nLength = strS.size();
int nCount = ;
int nPoss = ;
int nPost = ; while(nPoss < nLength)
{
if ( strS[nPoss] == strT[nPost] )
{
nPoss++;
nPost++;
}
else
{
nPost = vecNext[nPost];
if (nPost == -)
{
nPoss++;
nPost++;
}
} if (nPost == nSize )
{
nCount++;
nPost = ;
}
}
return nCount;
} int Algorithms::kmp2(string strS,string strT)
{
int nSize = strT.size();
vector<int> vecNext(nSize);
int i = ;
vecNext[] = -;
int j = -;
while(i<nSize-)
{
if (j==- || strT[i]==strT[j])
{
i++;
j++;
vecNext[i] = j;
}
else
j = vecNext[j];
}
for(int i=;i<nSize;i++)
cout<<"the vector is:"<<i<<": "<<vecNext[i]<<endl; int nLength = strS.size();
int nCount = ;
int nPoss = ;
int nPost = ; while(nPoss < nLength)
{
if ( strS[nPoss] == strT[nPost] )
{
nPoss++;
nPost++;
}
else
{
nPost = vecNext[nPost];
if (nPost == -)
{
nPoss++;
nPost++;
}
} if (nPost == nSize )
{
nCount++;
nPost = ;
}
}
return nCount;
} int Algorithms::kmp3(string strS,string strT)
{
int nSize = strT.size();
vector<int> vecNext(nSize);
int i = ;
vecNext[] = -;
int j = -;
while(i<nSize-)
{
if (j==- || strT[i]==strT[j])
{
i++;
j++;
if (strT[i] == strT[j])
vecNext[i] =vecNext[j];
else
vecNext[i] = j;
}
else
j = vecNext[j];
}
for(int i=;i<nSize;i++)
cout<<"the vector is:"<<i<<": "<<vecNext[i]<<endl; int nLength = strS.size();
int nCount = ;
int nPoss = ;
int nPost = ; while(nPoss < nLength)
{
if ( strS[nPoss] == strT[nPost] )
{
nPoss++;
nPost++;
}
else
{
nPost = vecNext[nPost];
if (nPost == -)
{
nPoss++;
nPost++;
}
} if (nPost == nSize )
{
nCount++;
nPost = ;
}
}
return nCount;
}
 //main.cpp
#include <iostream>
#include <string>
#include <vector>
#include "Algorithms.h"
using namespace std; void main()
{
string str1,str2;
cout<<"please input str1:"<<endl;
cin>>str1;
cout<<"please input str2:"<<endl;
cin>>str2;
//cout<<"the number of substr in str1 is:"<<Algorithms::kmp1(str1,str2)<<endl;
//cout<<"the number of substr in str1 is:"<<Algorithms::kmp2(str1,str2)<<endl;
cout<<"the number of substr in str1 is:"<<Algorithms::kmp3(str1,str2)<<endl;
system("pause");
}

算法评析:

1:Kmp1

先说下我自己写的吧,代码没有书上的简洁,再说说几个为什么。为什么循环要从i=2开始?

因为要用到int k = vecNext[i-1],以及strT[k] != strT[i-1],如果从i=1开始的话,k的起始值=-1,这样就会出现越界的情况,所以就从i=2开始;另外

next(0)=-1,next(1)=0,这都是毫无疑问的东西,所以可以在前两者已知的情况下,向后求解。

2:Kmp2

Kmp2的巧妙之处在于用了while循环,在需要i变化时才变化,否则已知变换j的值(j表示的是next(i))

3:Kmp3

Kmp3是对kmp2的改进,它考虑到了这样的一种情况:首先在T(i) = T(j)的情况下,如果按照kmp2那么next(i+1)= j+1。但是如果T(i+1)=T(j+1)的话,那么此时就无需在拿T(j+1) 跟母串的比较,因为T(i+1)已经比较过了,并且他们不相等,所以不需要再比较T(j+1),

只需要令:       next(i+1) = next(j+1)。

按照kmp2其实是这样的:  next(i+1) = j+1,j+1 = next(j+1),

所以中间省略的一步就是:next(i+1)= j+1。

总结:

关于kmp网上的讲解很多,但是坑爹的错误程序也不少,在验证一个kmp算法程序是否坑爹,只需要把next数组的内容打印出来,将它和自己口算得到的结果比对。百度百科的C++实现的kmp算法就是错误的,坑爹的。

kmp算法详解的更多相关文章

  1. &lbrack;转&rsqb; KMP算法详解

    转载自:http://www.matrix67.com/blog/archives/115 KMP算法详解 如果机房马上要关门了,或者你急着要和MM约会,请直接跳到第六个自然段.    我们这里说的K ...

  2. KMP算法详解&lpar;转自中学生OI写的。。ORZ!&rpar;

    KMP算法详解 如果机房马上要关门了,或者你急着要和MM约会,请直接跳到第六个自然段. 我们这里说的KMP不是拿来放电影的(虽然我很喜欢这个软件),而是一种算法.KMP算法是拿来处理字符串匹配的.换句 ...

  3. 算法进阶面试题01——KMP算法详解、输出含两次原子串的最短串、判断T1是否包含T2子树、Manacher算法详解、使字符串成为最短回文串

    1.KMP算法详解与应用 子序列:可以连续可以不连续. 子数组/串:要连续 暴力方法:逐个位置比对. KMP:让前面的,指导后面. 概念建设: d的最长前缀与最长后缀的匹配长度为3.(前缀不能到最后一 ...

  4. 数据结构4&period;3&lowbar;字符串模式匹配——KMP算法详解

    next数组表示字符串前后缀匹配的最大长度.是KMP算法的精髓所在.可以起到决定模式字符串右移多少长度以达到跳跃式匹配的高效模式. 以下是对next数组的解释: 如何求next数组: 相关链接:按顺序 ...

  5. KMP算法详解&amp&semi;&amp&semi;P3375 【模板】KMP字符串匹配题解

    KMP算法详解: KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt(雾)提出的. 对于字符串匹配问题(such as 问你在abababb中有多少个 ...

  6. 字符串匹配KMP算法详解

    1. 引言 以前看过很多次KMP算法,一直觉得很有用,但都没有搞明白,一方面是网上很少有比较详细的通俗易懂的讲解,另一方面也怪自己没有沉下心来研究.最近在leetcode上又遇见字符串匹配的题目,以此 ...

  7. KMP算法详解-彻底清楚了&lpar;转载&plus;部分原创&rpar;

    引言 KMP算法指的是字符串模式匹配算法,问题是:在主串T中找到第一次出现完整子串P时的起始位置.该算法是三位大牛:D.E.Knuth.J.H.Morris和V.R.Pratt同时发现的,以其名字首字 ...

  8. KMP算法详解 --- 彻头彻尾理解KMP算法

    前言 之前对kmp算法虽然了解它的原理,即求出P0···Pi的最大相同前后缀长度k. 但是问题在于如何求出这个最大前后缀长度呢? 我觉得网上很多帖子都说的不是很清楚,总感觉没有把那层纸戳破, 后来翻看 ...

  9. 字符串匹配的KMP算法详解及C&num;实现

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

随机推荐

  1. JSF标签的使用2

    n  事件监听器是用于解决只影响用户界面的事件 Ø  特别地,在beans的form数据被加载和触发验证前被调用 •    用immediate=“true”指明这个行为不触发验证 Ø  在监听器调用 ...

  2. NFS挂载操作指南

    NFS 全称 network file system,其功能是实现将某台服务器的某个目录下资源共享给其他服务器.被共享的服务器作为nfs服务端,需要开启和配置nfs server服务.共享他人资源的服 ...

  3. linux C学习笔记04--内存映射

    内存映射代码,打开一个文件与映射到内存中,对内存和文件的修改都会反映到文件中来,反之亦然,先贴代码,以后再完善: /****************************************** ...

  4. ORACLE建表练习

    1,学生表 -- Create table create table T_HQ_XS ( xueh ) not null, xingm ) not null, xingb ) ', nianl NUM ...

  5. 表单设置line-height,在ff中的不垂直居中问题???

    在ff中有时候input中的line-height,是有bug存在的,设置了line-height,发现文字并不是垂直居中. 1.这是正常现象,不需要刻意调整样式 2.以后尽量使用button,来避免 ...

  6. distinct数据去重关键字

    在表中,可能会包含重复值.这并不成问题,不过,有时您也许希望仅仅列出不同(distinct)的值.关键词 distinct用于返回唯一不同的值. 表A: 示例1 select distinct nam ...

  7. Hadoop-1&period;1&period;2、HBase-0&period;94&period;7完全分布式集群结构

    爱的技术可以应用到实际生活生产,做艺术向往的东西不腻和音乐. 现将前期手里面的一个项目做一个大致的总结,与大家一起分享.交流.进步. 项目如今正在线上执行,项目名--基于Hadoop的数据分析综合管理 ...

  8. C&num;Razor模板引擎简单使用

    引用 install-package RazorEngine 使用 public class TestDemo { private string name; public int Age { get ...

  9. CPU个数、CPU核心数、CPU线程数

    CPU个数.CPU核心数.CPU线程数 我们在选购电脑的时候,CPU是一个需要考虑到核心因素,因为它决定了电脑的性能等级.CPU从早期的单核,发展到现在的双核,多核.CPU除了核心数之外,还有线程数之 ...

  10. windows SysinternalsSuite

    procdump -ma -i  c\dumps 捕获系统所有程序的崩溃 SysinternalsSuite autoruns是个什么鬼