模式匹配算法KMP

1. kmp算法要解决什么问题

    有两个字符串str1和str2,现在要求查找str1中是否包含和str2相同的子串,如果存在,返回str1中子串的起始索引,如果没有,返回-1。

2. 暴力的解法

    比如str1为 abcabcqwerty
        str2为 abcq
    暴力解法为:
            首先,将str1和str2起始位置"对齐"
                a b c a b c q w e r t y
                a b c q
            然后逐个字符比较。发现在str1[3]和str2[3]位置处发生了不匹配。
            于是将str2的起始位置向后错开一位
               a b c a b c q w e r t y
                 a b c q
            然后接着逐个字符比较。

3. 为什么暴力解法不是最优解

    还是举个栗子:
        str1 : a b c a b c a b c k
        str2 : a b c a b c k

    很显然,第一次发生不匹配的时刻发生在这种情况
        a b c a b c a b c k
        a b c a b c k
    a不等于k

    那么按照暴力解法,接下来的
    a b c a b c a b c k
      a b c a b c k
    和
    a b c a b c a b c k
        a b c a b c k
    都要进行判断,但是我们很清楚,这两种情况下根本不可能完成匹配,所以优化的思路就在于如何在匹配过程中抛弃掉这些不可能完成匹配的过程。

4. 最大前缀和最大后缀的概念

    继续使用上面的例子。
    str1 : a b c a b c a b c k
    str2 : a b c a b c k

    说明什么是最大前缀和最大后缀
    我们将求str2[6]的最大前缀和最大后缀
    str2[6]之前的字符串为a b c a b c 我称之为 strtemp
    在这种情况下长度为1的前缀是 a
                长度为1的后缀是 c
                长度为2的前缀是 ab
                长度为2的后缀是 bc
                长度为3的前缀是 abc
                长度为3的后缀是 abc
                长度为4的前缀是 abca
                长度为4的后缀是 cabc
                长度为5的前缀是 abcab
                长度为5的后缀是 bcabc
        在此人为规定,前缀不看最后一个字符,后缀不看第一个字符。(因为如果考虑这种情况,长度为6的时候前后缀为本身,一定是相等的,没有意义,所以不考虑)

        这时我们发现当长度为3 的时候,前后缀相等,所以我们说str2[6] 的最大相等前后缀长度为3.

5. 如何使用最大相等前后缀长度来加速匹配过程

继续使用上面的例子。
    str1 : a b c a b c a b c k
    str2 : a b c a b c k
发现在str2[6]位置发生了不匹配现象。
已知str2[6]的最大相等前后缀长度为3.
也就是说,我们已经知道在str2[6]之前的字符和str1对应的字符应该是一样的,只是到6这个位置才发生了不同。
所以在这里我直接将str2向后移动3个位置(也就是移动了最大相等前后缀长度个位置)
得到的结果是
        a b c a b c a b c k
              a b c a b c k
这样操作就省略掉了暴力解法中不可能的匹配过程。
可以简单的反证一下。
如果之前那么按照暴力解法
    a b c a b c a b c k
      a b c a b c k
    和
    a b c a b c a b c k
        a b c a b c k

这两个步骤如果有可能匹配成功的话
说明长度为5的最大前后缀或者长度为4的最大前后缀应该会相等。
也就是说,加速的思路就是寻找最大相等的前后缀,将前缀移动到后缀的位置,这样就省略了很多不可能的匹配过程,并且接下来,我可以直接匹配str2[3]和str1对应位置的匹配情况了。

6. 最大相等前后缀长度——next数组

    在第4部分提出的最大相等前后缀长度,是一种易于理解但是操作繁琐的方式,在实际的加速过程中,并不是这样求的。
    在实际的kmp算法中,我们将这个最大相等前后缀长度,命名为next数组。

    举个例子
        str2 为:a b a c a b a b a

    next[i]对应str2[i]
    首先说明在next数组中,next[0]=-1,next[1] = 0,这是人为规定,至于为什么一会再说。
    接下来假设我们已知next数组中,next数组的0~6为[-1,0,0,1,0,1,2]。
    那么如何根据已知求next[7]呢?
    答案是根据next[6]求next[7]。
        因为next[6] = 2,说明在str2[5] == str2[1],str2[4] == str2[0].
        那么接下来,发现str2[6] == str2[2],所以next[7]的值将是next[6]+1;
        但是如果str2[6] != str2[3]怎么办呢?

        比如

        可以看到如图这样的情况,在求a位置的next的时候,发现 c位置和b位置不相等(假设两个黄圈是b位置的最大相等的前后缀,绿圈是c位置的最大相等前后缀)
        很显然,对应颜色的圈里面的内容应该相等。
        实际上,后面的黄圈中,应该也有对应的两个绿圈存在(并且完全相等)
        那么如果c!=b,且c`和b相等,根据对应关系,可以求得a位置的next就等于c位置next+1.
        如果c`仍然不等于b那么就可以迭代下去,寻找c``,c```等,只要找到一个和b相等的就成功的确定了这个位置的next值,如果迭代到str2[0]的位置,仍然没有(也就是next值搜到-1了)那么说明next 等于 0。

        好了,啰嗦了一大堆,下面整理一下求法。
        1.next[0] = -1,next[1] = 0,人为规定。
        2.已知next[i],求next[i+1];
        3.将str2[i]和str2[next[i]]比较。
                具体如下
                用c表示str2[next[i]]
                  nc表示next[i]
                while(nc != -1 && str2[i-1] != c)
                {
                    nc = next[nc];
                    if(nc == -1) break;
                    c = str2[nc];
                }
                next[i+1] = nc+1;
        这样就是求解next数组的基本思路。
        

7. 代码

/*
    vs2012 c++语言
    代码有些啰嗦,但是严格按照上述分析过程完成
    完美代码实现在互联网上很多,就不写了。
    测试用例:
        string str1 = "12345abacababa2134567";
        string str2 = "abacababa";
    结果:5
    结果正确。
*/
vector<int> getnext(string& str2)
{
    vector<int> next;
    next.push_back(-1);
    next.push_back(0);
    int length = str2.length();
    for(int i = 1;i<length-1;++i)
    {
        char c = str2[next[i]];
        int nc = next[i];
        while(nc != -1 && str2[i] != c)
        {
            nc = next[nc];
            if(nc == -1) break;
            c = str2[nc];
        }
        next.push_back(nc+1);
    }
    return next;
}

int kmp(string& s1,string& s2)
{
    int it1 = 0,it2 = 0;
    int ret = -1;
    vector<int> next = getnext(s2);
    while(it1<s1.length() && it2 < s2.length())
    {
        if(s1[it1] == s2[it2])
        {
            it1++;
            it2++;
            continue;
        }
        else
        {
            if(next[it2] == -1)
                it1++;
            else
                it2 = next[it2];
        }
    }
    if(it2 == s2.length())
        return it1-it2;
    else
        return -1;
}
int main()
{
    string str1 = "12345abacababa2134567";
    string str2 = "abacababa";
    cout<<kmp(str1,str2);
    system("pause");
    return 0;
}
时间: 2024-10-21 04:53:30

模式匹配算法KMP的相关文章

《数据结构》之串的模式匹配算法——KMP算法

1 //串的模式匹配算法 2 //KMP算法,时间复杂度为O(n+m) 3 #include <iostream> 4 #include <string> 5 #include <cstring> 6 using namespace std; 7 8 //-----串的定长顺序存储结构----- 9 #define MAXLEN 255 //串的最大长度 10 typedef struct { 11 char ch[MAXLEN + 1]; //存储串的一维数组 12

Java数据结构之字符串模式匹配算法---KMP算法

本文主要的思路都是参考http://kb.cnblogs.com/page/176818/ 如有冒犯请告知,多谢. 一.KMP算法 KMP算法可以在O(n+m)的时间数量级上完成串的模式匹配操作,其基本思想是:每当匹配过程中出现字符串比较不等时,不需回溯指针,而是利用已经得到的"部分匹配"结果将模式向右"滑动"尽可能远的一段距离,继续进行比较.显然我们首先需要获取一个"部分匹配"的结果,该结果怎么计算呢? 二.算法分析 在上一篇中讲到了BF算法,

Java数据结构之字符串模式匹配算法---KMP算法2

直接接上篇上代码: 1 //KMP算法 2 public class KMP { 3 4 // 获取next数组的方法,根据给定的字符串求 5 public static int[] getNext(String sub) { 6 7 int j = 1, k = 0; 8 int[] next = new int[sub.length()]; 9 next[0] = -1; // 这个是规定 10 next[1] = 0; // 这个也是规定 11 // 12 while (j < sub.l

串的模式匹配算法 ------ KMP算法

1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 int* get_next(char t[], int length) 6 { 7 int i = 0, j = -1; 8 int* next = (int *)malloc(length * sizeof(int)); 9 next[0] = -1; 10 while (i < length) 11 { 12 if (j =

朴素和KMP模式匹配算法(Java)

朴素模式匹配算法 public class Test { //朴素模式匹配算法 public int Index(String s,String t,int pos){ int i = pos;//主串中第几个位置开始比较 int j = 0;//模式串中的第一个位置 while(i<s.length()&&j<t.length()){ if(s.charAt(i)==t.charAt(j)){ i++; j++; }else { i = i-j+1;//主串的下一个位置 j

浅谈数据结构之KMP(串中的模式匹配算法)

KMP算法是一种模式匹配算法的改进版,其通过减少匹配的次数以及使主串不回朔来减少字符串匹配的次数,从而较少算法的相应代价,但是,事件万物是普遍归中的,KMP算法的有效性也是有一定的局限的,我将在本文的最后也讨论这个算法的局限性. 一般的匹配算法: KMP基本概念引入: 但是,其实我们会发现,上面的中间两个匹配步骤是没有必要的,因为他们的第一个匹配字母就不相同,完全没有可比性,而当我们在第四次匹配的时候,其实我们从模式串中就可得知,只有当模式串滑到这个地方的时候,它的匹配才是最有价值的,因为从模式

[转] 字符串模式匹配算法——BM、Horspool、Sunday、KMP、KR、AC算法一网打尽

字符串模式匹配算法——BM.Horspool.Sunday.KMP.KR.AC算法一网打尽 转载自:http://dsqiu.iteye.com/blog/1700312 本文内容框架: §1 Boyer-Moore算法 §2 Horspool算法 §3 Sunday算法 §4 KMP算算法 §5 KR算法 §6 AC自动机 §7 小结 §1 Boyer-Moore(BM)算法 Boyer-Moore算法原理 Boyer-Moore算法是一种基于后缀匹配的模式串匹配算法,后缀匹配就是模式串从右到

第四章:2.串 -- 串的模式匹配算法(KMP)

前言: 目录: 1.串类型的定义 2.串的表示和实现 3.串的模式匹配算法 4.串操作应用举例 正文: 串的模式匹配即,在给定主串S 中,搜索子串T 的位置,如果存在T 则返回其所在位置,否则返回 0 串的模式匹配算法 主串 S: a b c a b c d s v t 子串 T: a b c d 一.原始算法 匹配一旦失败,子串即向右移动一个单位,直到完全匹配停止. 第一次匹配:(注:红色代表不匹配(失配)) S: a b c a b c a b c d s v t   T: a b c d

串的模式匹配算法(KMP)

算法: #include<IOSTREAM> using namespace std; #define MAXSIZE 100 void calNext(const char *T,int *next);//T为模式串,next为预判数组 int kmp_match(const char *S,const char *T);//在主串S中寻找模式串T,如果找到返回其位置,否则返回-1.位置从0开始 void calNext(const char *T,int *next) { int n =