KMP算法 (字符串的匹配)

视频参考

对于正常的字符串模式匹配,主串长度为m,子串为n,时间复杂度会到达O(m*n),而如果用KMP算法,复杂度将会减少线型时间O(m+n)。

设主串为ptr="ababaaababaa";,要比较的子串为a=“aab”;

KMP算法用到了next数组,然后利用next数组的值来提高匹配速度,我首先讲一下next数组怎么求,之后再讲匹配方式。

next数组详解

首先是理解KMP算法的第一个难关是next数组每个值的确定,这个问题困恼我很长时间,尤其是对照着代码一行一行分析,很容易把自己绕进去。

定义一串字符串

ptr = "ababaaababaa";

next[i](i从1开始算)代表着,除去第i个数,在一个字符串里面从第一个数到第(i-1)字符串前缀与后缀最长重复的个数。

什么是前缀?

在“aba”中,前缀就是“ab”,除去最后一个字符的剩余字符串。

同理可以理解后缀。除去第一个字符的后面全部的字符串。

在“aba”中,前缀是“ab”,后缀是“ba”,那么两者最长的子串就是“a”;

在“ababa”中,前缀是“abab”,后缀是“baba”,二者最长重复子串是“aba”;

在“abcabcdabc”中,前缀是“abcabcdab”,后缀是“bcabcdabc”,二者最长重复的子串是“abc”;

这里有一点要注意,前缀必须要从头开始算,后缀要从最后一个数开始算,中间截一段相同字符串是不行的。

再回到next[i]的定义,对于字符串ptr = "ababaaababaa";

next[1] = -1,代表着除了第一个元素,之前前缀后缀最长的重复子串,这里是空 ,即"",没有,我们记为-1,代表空。(0代表1位相同,1代表两位相同,依次累加)。

next[2] = -1,即“a”,没有前缀与后缀,故最长重复的子串是空,值为-1;

next[3] = -1,即“ab”,前缀是“a”,后缀是“b”,最长重复的子串“”;

next[4] = 1,即"aba",前缀是“ab”,后缀是“ba”,最长重复的子串“a”;next数组里面就是最长重复子串字符串的个数

next[5] = 2,即"abab",前缀是“aba”,后缀是“bab”,最长重复的子串“ab”;

next[6] = 3,即"ababa",前缀是“abab”,后缀是“baba”,最长重复的子串“aba”;

next[7] = 1,即"ababaa",前缀是“ababa”,后缀是“babaa”,最长重复的子串“a”;

next[8] = 1,即"ababaaa",前缀是“ababaa”,后缀是“babaaa”,最长重复的子串“a”;

next[9] = 2,即"ababaaab",前缀是“ababaaa”,后缀是“babaaab”,最长重复的子串“ab”;

next[10] = 3,即"ababaaaba",前缀是“ababaaab”,后缀是“babaaaba”,最长重复的子串“aba”;

next[11] = 4,即"ababaaabab",前缀是“ababaaaba”,后缀是“babaaabab”,最长重复的子串“abab”;

next[12] = 5,即"ababaaababa",前缀是“ababaaabab”,后缀是“babaaaababa”,最长重复的子串“ababa”;

还有另外一种方法,我看的有的书上写着:

这里我们定义next[1] = 0 , next[1] = 1;

再分析ptr字符串,ptr = "ababaaababaa";

跟上一个的情况类似,

next[1] = 0 ,事先定义好的

next[2] = 1 ,事先定义好的

next[3] = 1 ,最长重复的子串“”;1代表没有重复,2代表有一个字符重复。

next[4] = 2 ,最长重复的子串“a”;追偿的长度加1,即为2.

next[5] = 3 ,以下都跟之前的一样,这种方法是最长的长度再加上一就可以了。

next[6] = 4

next[7] = 2

next[8] = 2

next[9] = 3

next[10] = 4

next[11] = 5

next[12] = 6

以上是next数组的详细解释。next数组求值 是比较麻烦的,剩下的匹配方式就很简单了。

next数组用于子串身上,根据上面的原理,我们能够推出子串a=“aab”的next数组的值分别为0,1,2.(按照我说的第二种方式算的)。

首先开始计算主串与子串的字符,设置主串用i来表示,子串用j来表示,如果ptr[i]与a[i]相等,那么i与j就都加1:

prt[1]与a[1]相等,i++,j++:

用代码实现就是

  1. if( j==0 || ptr[i]==a[j])

  2.  

    {

  3.  

    ++i;

  4.  

    ++j;

  5.  

    }

ptr[2]与a[2]不相等

此时ptr[2]!=a[2],那么令j = next[j],此时j=2,那么next[j] = next[2] = 1.那么此时j就等于1.这一段判断用代码解释的话就是:

  1. if( ptr[i]!=a[j])

  2.  

    {

  3.  

    j = next[j];

  4.  

    }

加上上面的代码进行组合:

在对两个数组进行比对时,各自的i,j取值代码:

  1. while( i<ptr.length && j< a.length)

  2.  

    {

  3.  

    if( j==0 || ptr[i]==a[i] )

  4.  

    {

  5.  

    ++i;

  6.  

    ++j;

  1. next[i] = j;

  2.  

    }

  3.  

    else

  4.  

    {

  5.  

    j = next[j];

  6.  

    }

  7.  

    }

此时将a[j]置于j此时所处的位置,即a[1]放到j=2处,因为在j=2时出现不匹配的情况。

此时再次计算是否匹配,可以看出来a[1]!=ptr[2],那么j = next[j],即此时j = next[1] = 0;

根据上面的代码,当j=0时,执行++i;++j;

此时就变为:

此时ptr[3] = a[1],继续向下走,下一个又不相等了,然后“aab”向后挪一位,这里不再赘述了,主要的思想已经讲明白了。到最后一直到i = 8,j=3时匹配成功,KMP算法结束。整个过程就结束了。

#include<bits/stdc++.h>

using namespace std;
const int maxn = 1000000+10;
int Next [maxn];
char str[maxn],mo[maxn];
void getNext()
{
    int i = 0,j = -1 , len = strlen(mo) ;
    while(i<len)
    {
        if(j==-1||mo[i] == mo[j]){Next[++i] = ++j;}
        else
            j = Next[j];
    }
}
int kmp()
{
    int i=0,j=0,l1=strlen(str),l2=strlen(mo);
    int ans = 0;
    while(i<l1)
    {
        if(j==-1 || mo[j] == str[i]) i++,j++;
        else j = Next[j];
        if(j == l2) ans++;
    }
    return ans ;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s%s",mo,str);
        Next[0] = -1 ;
        getNext();
        printf("%d\n",kmp());
    }
}

原文地址:https://www.cnblogs.com/shuaihui520/p/9378010.html

时间: 2024-10-25 19:44:42

KMP算法 (字符串的匹配)的相关文章

KMP算法,查询匹配串的个数

想不到时隔两年回来重新学习KMP算法还是那么难,不过理解了大概,把例程贴上来,如果是求数量只需要加个count变量记录即可. #include"stdio.h" #include"string.h" void makeNext(const char P[],int next[]) { int q,k; int m=strlen(P); next[0]=0; for(q=1,k=0;q<m;q++) { while(k>0&&P[q]!=P

子字符查找KMP算法 - 子串自匹配索引表

public static int[] kmpTable(char[] seq) { int[] tbl = new int[seq.length]; tbl[0] = 1; for (int i = 1; i < seq.length; i++) {// 子串最开始 int j = tbl[i - 1];// 从已经算出的索引开始 l1: for (; j <= i; j++) { for (int k = 0; j + k <= i; k++) { if (seq[j + k] ==

字符串模式匹配之KMP算法图解与 next 数组原理和实现方案

之前说到,朴素的匹配,每趟比较,都要回溯主串的指针,费事.则 KMP 就是对朴素匹配的一种改进.正好复习一下. KMP 算法其改进思想在于: 每当一趟匹配过程中出现字符比较不相等时,不需要回溯主串的 i指针,而是利用已经得到的“部分匹配”的结果将模式子串向右“滑动”尽可能远的一段距离后,继续进行比较.如果 ok,那么主串的指示指针不回溯!算法的时间复杂度只和子串有关!很好. KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的,很自然的,需要一个函数来存储匹

字符串 - KMP算法

字符串算法中,字符串匹配是一个非常重要的应用.例如在网页中查找关键词,其实就是在对字符串匹配,也就是看一个主字符串中是否包含了一个子字符串. 而KMP算法在字符串匹配方法中一个很著名并且很聪明的算法,当然也确实比较难理解.甚至于有程序员因为无法理解KMP算法而直接改用暴力匹配.本身自己学算法起步较晚,第一次接触到KMP算法已经是研究生毕业一年了.虽然带着研究生的学历背景,但是刚开始看的时候依然是一脸懵逼.看了很多博主的讲解总算是明白了,所以在这篇博客中记录下来自己的理解,如果能帮助到别人也是万分

KMP算法-Java实现

目的: 为了解决字符串模式匹配 历程: 朴素模式匹配:逐次进行比较 KMP算法:利用匹配失败得到的信息,来最大限度的移动模式串,以此来减少比较次数提高性能 概念: m:是目标串长度 n:是模式串长度 j:某次匹配时,第一次出现的不同的索引位置(有的称为:失配位) k:最长首尾串长度(有的称为:最长公共前后缀) 核心思想: S   S0 S1 ...... Si-j-1 Si-j Si-j+1 Si-j+2 ...... Si-2 Si-1 Si ...... Sn-1 ||     ||    

菜鸟学算法-KMP算法

一. KMP算法 KMP算法是一种改进的字符串匹配算法,由D.E.Knuth与V.R.Pratt和J.H.Morris同时发现,简称KMP算法.KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的.具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息. 二. KMP算法的意义 先举一个简单模式匹配的例子,给定字符串T=“abababca”,S=“bacbababaabcbab”,判断T是否是S的子串,如果用暴力扫描的话,就是拿着T字符串从

KMP算法浅析

背景: KMP算法之所以叫做KMP算法是因为这个算法是由三个人共同提出来的,就取三个人名字的首字母作为该算法的名字.其实KMP算法与BF算法的区别就在于KMP算法巧妙的消除了指针i的回溯问题,只需确定下次匹配j的位置即可,使得问题的复杂度由O(mn)下降到O(m+n). KMP算法的思想就是:在匹配过程称,若发生不匹配的情况,如果next[j]>=0,则目标串的指针i不变,将模式串的指针j移动到next[j]的位置继续进行匹配:若next[j]=-1,则将i右移1位,并将j置0,继续进行比较.

字符串的KMP算法替换

1 #include<iostream> 2 #include<string> 3 using namespace std; 4 5 6 7 class myString 8 { 9 private: 10 string mainstr; 11 int size; 12 void GetNext(string p,int next[]); 13 int KMPFind(string p,int next[]); 14 public: 15 myString(); 16 //~myS

字符串模式匹配KMP算法中的next数组算法及C++实现

一.问题描述: 对于两个字符串S.T,找到T在S中第一次出现的起始位置,若T未在S中出现,则返回-1. 二.输入描述: 两个字符串S.T. 三.输出描述: 字符串T在S中第一次出现的起始位置,若未出现,则返回-1. 四.输入例子: ababaababcbababc 五.输出例子: 5 六.KMP算法解析: KMP算法分为两步,第一步是计算next数组,第二步是根据next数组通过较节省的方式回溯来比较两个字符串. 网络上不同文章关于next数组的角标含义略有差别,这里取参考文献中王红梅<数据结构