从暴力匹配到KMP算法

前言

现在有两个字符串:\(s1\)和\(s2\),现在要你输出\(s2\)在\(s1\)当中每一次出现的位置,你会怎么做?


暴力匹配算法

基本思路

用两个指针分别指向当前匹配到的位置,并对当前状态进行分类讨论:若相同则继续往下匹配,否则回溯

大致思路

用\(i\)来存储\(s1\)当前匹配到的位置,用\(j\)来存储\(s2\)当前匹配到的位置,则可得初始状态下\(i=j=0\)。

对于当前状态,有两种可能性:

①:\(s1[i]==s2[j]\)。则\(i++,j++\)

②:\(s1[i]!=s2[j]\)。则\(i-=(j-1),j=0\)

评价

时间复杂度:\(O(nm)\)。显然,这个方法效率并不高,每一次回溯要耗去大量时间,能不能进行优化呢?


\(KMP\)算法

简介

\(KMP\)算法是对暴力匹配算法的改进,由\(D.E.Knuth\),\(J.H.Morris\)和\(V.R.Pratt\)同时发现,因此人们称它为\(Knuth-Morris-Pratt\)算法(简称\(KMP\)算法)。

基本思路

\(KMP\)算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个\(Next\)函数,函数本身包含了模式串的局部匹配信息。

大致思路

还是用\(i\)来存储\(s1\)当前匹配到的位置,用j来存储\(s2\)当前匹配到的位置,则可得初始状态下\(i=j=0\)
对于当前状态,有两种可能性:
①:\(s1[i]==s2[j]\)。则\(i++,j++\)
②:\(s1[i]!=s2[j]\)。则\(j=Next[j]\)(i不变)
其中\(Next\)数组存储的是当前这一位的部分匹配值(这在后面会详细介绍),所以只要让\(j\)变成\(Next[j]\),就可以继续对当前字符串进行匹配了,省去了i回溯所耗去的大量时间

\(Next\)数组

在匹配过程中,你可以发现一个基本事实是:当\(s1[i]\)与\(s2[j]\)不匹配时,你其实知道前面\(j-1\)字符是什么。

\(KMP\)算法的想法是,设法利用这个已知信息,不要把"搜索位置"移回已经比较过的位置,继续把它向后移,这样就提高了效率。

所以,我们就可以把当前所得到的部分匹配值给求出来。又由于对于同一个字符串,部分匹配值是固定不变的,所以可以把它存在\(Next\)数组里。

那么\(Next\)数组怎么求呢?

\(Excerpt\)

求\(Next\)数组的过程就是一个\(KMP\)的过程。

首先,令\(i=0\),\(j=-1\),\(Next[0]=-1\),且当前要求的是\(Next[i+1]\)。则对于当前状态,有两种可能性:
①\(j==-1\)或\(s2[i]==s2[j]\)。则\(i++,j++,Next[i]=j\)
②\(j!=-1\)且\(s2[i]!=s2[j]\)。则\(j=Next[j]\)//把\(j\)赋值为\(j\)的部分匹配值
这样就可以轻松求出\(Next\)数组了。

代码
#include<bits/stdc++.h>
#define N 1000000
#define pc(ch) (pp_<100000?pp[pp_++]=ch:(fwrite(pp,1,100000,stdout),pp[(pp_=0)++]=ch))
int pp_=0;char pp[100000];
using namespace std;
int len1,len2,Next[N+5];//len1存储s1的长度,len2存储s2的长度,这样不用调用strlen(),strlen()会超时;Next[]存储部分匹配值
char s1[N+5],s2[N+5];
inline void write(int x)
{
    if(x>9) write(x/10);
    pc(x%10+'0');
}
inline void GetNext()//求出Next[]数组
{
    register int i=0,j=Next[0]=-1;//初始化
    while(i<=len2)//类似于一个KMP的过程
    {
        if(j==-1||s2[i]==s2[j]) i++,j++,Next[i]=j;
        else j=Next[j];
    }
}
int main()
{
    register int i=0,j=0;
    scanf("%s%s",s1,s2),len1=strlen(s1),len2=strlen(s2),GetNext();
    while(i<=len1)//KMP的过程
    {
        if(j==-1||s1[i]==s2[j]) {++i;if(++j==len2) write(i-len2+1),pc('\n'),j=Next[j];/*如果找到答案就输出*/}
        else j=Next[j];//如果匹配失败,就更新j为其部分匹配值
    }
    for(i=1;i<=len2;++i) write(Next[i]),pc(' ');//依照题意输出Next[]数组
    return fwrite(pp,1,pp_,stdout),0;
}

原文地址:https://www.cnblogs.com/chenxiaoran666/p/KMP.html

时间: 2024-08-29 22:59:48

从暴力匹配到KMP算法的相关文章

串的匹配:朴素匹配&amp;amp;KMP算法

引言 字符串的模式匹配是一种经常使用的操作. 模式匹配(pattern matching),简单讲就是在文本(text,或者说母串str)中寻找一给定的模式(pattern).通常文本都非常大.而模式则比較短小.典型的样例如文本编辑和DNA分析. 在进行文本编辑时,文本一般是一段话或一篇文章,而模式则经常是一个单词.若是对某个指定单词进行替换操作,则要在整篇文章中进行匹配,效率要求肯定是非常高的. 模式匹配的朴素算法 最简单也最easy想到的是朴素匹配.何为朴素匹配,简单讲就是把模式串跟母串从左

字符串匹配的算法(暴力算法和KMP算法)

学习字符串匹配算法有一段时间了,不过还是有点迷糊,虽然了解算法过程,但是在编码的时候还是会有些迷糊. 先把写的程序放在这里,以后有时间再来翻着看看吧! #include<iostream> #include<string> using namespace std; int KMPfind(char* s, char* p); void GetNext(char* p, int next[]); int ViolentMatch(char* s, char* p); int main

字符串匹配暴力算法 与 字符串匹配的KMP算法

声明:先看一下阮一峰的网络日志关于字符串的KMP算法的讲解.本文图片均引用于这篇日志. 在先前的笔试中遇到了关于字符串匹配的问题,一时脑袋卡壳没写好算法.现在就来分析分析 暴力算法和KMP算法各自原理,以及代码实现,之间差异,并且总结一下好算法的一般思路. =========================================================================== 各自原理: 暴力算法: 1. 我们把长的字符串做为一个文本字符串,命名为strText,把

关于KMP算法的认识

目前大二,大一时年自己纯属划水度过,身为一个学计算机的可能连一些没学的还要差.感觉自己不能在这样颓废下去了,是时候要努力一波了. 决定开始从算法开始补起. 大一数据结构在字符串匹配的时候曾讲过,当时对计算机还处于相当懵逼的状态,自然也就不会.前几天看算法题又一次看到了,决定把它补回来. KMP算法 首先要先看 BF算法--最简单直观的模式匹配算法 如果用BF暴力匹配的思路 算法步骤: 分别利用计数指针i , j  指示主串S和模式T中当前正待比较的字符位置pos,j初值为0: 如果当前字符匹配成

计算子串在主串中的位置及其优化(KMP算法)

问题描述:设置一个起始位置,寻找主串中第一次出现子串的首位置. 算法实现: int index(string str,string substr,int pos) { int i=0,j=0; int slen,sslen; i=pos; slen=str.length(); sslen=substr.length(); while(i+sslen<slen) { while(j<sslen) { if(str[i+j]==substr[j]) j++; else break; } if(j=

数据结构与算法JavaScript (五) 串(经典KMP算法)

数据结构与算法JavaScript (五) 串(经典KMP算法) KMP算法和BM算法 KMP是前缀匹配和BM后缀匹配的经典算法,看得出来前缀匹配和后缀匹配的区别就仅仅在于比较的顺序不同 前缀匹配是指:模式串和母串的比较从左到右,模式串的移动也是从 左到右 后缀匹配是指:模式串和母串的的比较从右到左,模式串的移动从左到右. 通过上一章显而易见BF算法也是属于前缀的算法,不过就非常霸蛮的逐个匹配的效率自然不用提了O(mn),网上蛋疼的KMP是讲解很多,基本都是走的高大上路线看的你也是一头雾水,我试

KMP算法学习笔记

http://www.matrix67.com/blog/archives/115 Orz maxtix67 一直拖着到碰到了KMP算法的题才学.. 设有字符串A:abefgababef 模式串 B:abefgabef 当A与B进行匹配时,设i是A上的指针,j是B上的指针,则A和B匹配到第8位就会失配.按照常规方法,我们会将i跳回到2,j跳回到1进行匹配.观察之后我们会发现其实这是多余的,因为中间的befg都是不可能的,我们应该直接跳到A的第6位进行匹配. KMP算法就是通过过滤这样无用字符来加

模式串匹配、KMP算法及其改进(代码)

#include "string.h" #include "stdio.h" #include "stdlib.h" #include "io.h" #include "math.h" #include "time.h" #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXSIZE 100 /* 存

KMP算法匹配原理以及C++实现

原创作品,转载请注明出处:点我 假设A表示目标字符串,A="abababaababacb",B表示匹配模式,B="ababacb" 用两个指针i和j分别表示,A[i-j+1 .... i]与B[1...j]完全相等.也就是说,i是不断增加的,随着i的增加j相应的变化,且满足以A[i]结尾的长度为j的字符串正好匹配B串的前j个字符(j当然越大越好),现在需要jianyanA[i+1]和B[j+1]的关系.当A[i+1]=B[j+1]时,i和j各自增加一,什么时候j=m