数据结构- KMP

Posted 123zhh-helloworld

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构- KMP相关的知识,希望对你有一定的参考价值。

数据结构 - KMP

引言 & 介绍

  • 由于李总说过串这一章只讲一个KMP, 所以我这里也就只说一个KMP算法了
  • KMP算法, 说得简单点就是关键字搜索

一般方法

  • 一般的关键字搜索的算法为:
 1 int Search(string a, string b) {
 2     int lena = a.length();
 3     int lenb = b.length();
 4     int i = 0, j = 0;
 5  
 6     while (i < lena && j < lenb) {
 7         if (a[i] == b[j]) {
 8             i++; j++;
 9         } else {
10             i = i - j + 1;
11             j = 0;
12         }
13     }
14     return j == lenb ? i - j + 1 : -1;
15 }

 

  • 这个方法的时间复杂度为O(lena * lenb), 可以说是相当高了, 我们可能觉得无所谓, 但是大牛们就会想一种新的算法来优化, 这就是KMP算法, KMP算法的时间复杂度为O(lena + lenb)

KMP算法

KMP函数

  • 如果主串a为“abcdabceabcdabcd”, 模式串b为“abcdabcd”, 一般方法在j = 7(从0开始)时就失配了, 但是, 一般方法就会执行很多无意义的部分, 现在, 我们把代码做一点修改:
int KMP(string a, string b) {
    int lena = a.length(), lenb = b.length();
    int i = 0, j = 0;
    int *next = GetNext(b);

    while (i < lena && j < lenb) {
        if (j == -1 || a[i] == b[j]) {
            i++;    j++;
        } else {
            j = next[j];
        }
    }
    return j == lenb ? i - j + 1 : -1;
}

 

求next数组

  • 这个代码, 当然是不完整的, 所以这个先看下面这个求next数组的代码:
 1 int* GetNext(string b) {
 2     int j = 0, k = -1;
 3     int len = b.length();
 4     int *next = new int[len + 1];
 5     next[0] = -1;
 6 
 7     while (j < len) {
 8         if (k == -1 || b[j] == b[k]) {
 9             j++;
10             k++;
11             next[j] = k;
12         } else {
13             k = next[k];
14         }
15     }
16     return next;
17 }

 


分析next数组

  • 分析next数组前, 我们再说说主串a为“abcdabceabcdabcd”, 模式串b为“abcdabcd”的情况
  • 当j = 7的时候a[7] 和 b[7]不匹配, 但是a[0 - 6]和b[0 - 6]是匹配的, 如果b[0 - 6]中的前x个字符和后x个字符一模一样, 那么, 我们就无需把j置零, 只需要把j置为x, 直接比较b[x]和a[7]就可以了
  • 而事实上, b[0 - 2]和b[4 - 6]是一样的, 这个x为3, 也可以直接看出, a[4 - 6]和b[0 - 2]是匹配的, 所以我们直接看a[7]和ab[3], 当然啦, 这里a[7] = e, b[3] = d也不匹配, 所以再执行这个步骤·······
  • 上面的x就是next[j], 在GetNext函数里面则是next[k]
  • 现在应该知道next数组的作用了吧, next[j]表示, 在a[i]与b[j]不匹配时, b的前next[j]个字符, 和后next[j]个字符完全相同
  • 知道了next数组的含义之后, 再看上面的函数应该就比较好理解了

GetNext的优化

  • 现在考虑a = “aaabaaabaaab”, b = “aaaa”的情况
  • next数组为{-1, 0, 1, 2, 3}, 这就意味着, j = 3时失配时, j会从3减到-1, 而不是直接从3跳到-1, 如此完美的算法在这一步出了这种情况岂不可惜, 所以, 这个算法还有最后一步优化
 1 int* GetNext(string b) {
 2     int j = 0, k = -1;
 3     int len = b.length();
 4     int *next = new int[len + 1];
 5     next[0] = -1;
 6 
 7     while (j < len) {
 8         if (k == -1 || b[j] == b[k]) {
 9             if (b[++j] == b[++k]) {
10                 next[j] = next[k];
11             } else {
12                 next[j] = k;
13             }
14         } else {
15             k = next[k];
16         }
17     }
18     return next;
19 }

 

  • 将GetNext略作修改就好了

代码

  • 下面给出完整的KMP算法的代码
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 int* GetNext(string b) {
 5     int j = 0, k = -1;
 6     int len = b.length();
 7     int *next = new int[len + 1];
 8     next[0] = -1;
 9 
10     while (j < len) {
11         if (k == -1 || b[j] == b[k]) {
12             if (b[++j] == b[++k]) {
13                 next[j] = next[k];
14             } else {
15                 next[j] = k;
16             }
17         } else {
18             k = next[k];
19         }
20     }
21     return next;
22 }
23 
24 int KMP(string a, string b) {
25     int lena = a.length(), lenb = b.length();
26     int i = 0, j = 0;
27     int *next = GetNext(b);
28 
29     while (i < lena && j < lenb) {
30         if (j == -1 || a[i] == b[j]) {
31             i++;    j++;
32         } else {
33             j = next[j];
34         }
35     }
36     return j == lenb ? i - j + 1 : -1;
37 }
38 
39 int main () {
40     string a, b;
41     while (cin >> a >> b) {
42         cout << KMP(a, b) << endl;
43     }
44     return 0;
45 }

 


以上是关于数据结构- KMP的主要内容,如果未能解决你的问题,请参考以下文章

kmp算法的个人理解

数据结构—串KMP模式匹配算法

KMP算法

学习数据结构笔记(18) --- [KMP算法]

数据结构KMP算法配图详解(超详细)

[数据结构]KMP算法