Fork me on GitHub

KMP算法讲解

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。时间复杂度O(m+n)。

以上是百度百科对KMP算法的介绍,如何把实现把朴素算法变成O(m+n)的时间复杂度呢,从上面介绍可以看出来,KMP算法利用了一个next数组,所以需要预处理,下面我们就来讲解KMP算法。

因为懒得画图还怕画不好,所以我录制成了视频的格式。

Bilibili视频:https://www.bilibili.com/video/av40137935

这是一道KMP裸题,请自行尝试AC:传送门

看完上面,你大致就应该清楚如何利用KMP进行线性匹配了,但是KMP算法的精髓其实不是进行简单的串匹配,精髓应该在于next数组的应用,以及扩展的next_val数组的运用,可以快速的寻找循环节,前缀匹配等等一些复杂的字符串问题。

下面将以一道例题说明next数组的强大

HDU 1358(Period)

Problem Description
For each prefix of a given string S with N characters (each character has an ASCII code between 97 and 126, inclusive), we want to know whether the prefix is a periodic string. That is, for each i (2 <= i <= N) we want to know the largest K > 1 (if there is one) such that the prefix of S with length i can be written as A K , that is A concatenated K times, for some string A. Of course, we also want to know the period K.
Input
The input file consists of several test cases. Each test case consists of two lines. The first one contains N (2 <= N <= 1 000 000) - the size of the string S. The second line contains the string S. The input file ends with a line, having the number zero on it.
Output
For each test case, output “Test case #” and the consecutive test case number on a single line; then, for each prefix with length i that has a period K > 1, output the prefix size i and the period K separated by a single space; the prefix sizes must be in increasing order. Print a blank line after each test case.
SampleInput
3
aaa
12
aabaabaabaab
0
 
SampleOutput
Test case #1
2 2
3 3

Test case #2
2 2
6 2
9 3
12 4

题意:给一个长为n的字符串,问字符串的前缀是不是周期串,如果是周期串,输出前缀的最后一个字母的位置和最短周期

应该如何思考呢,已经说明了这是一道KMP的题,用KMP进行串匹配吗?显然不是,那么肯定就是利用next数组的性质了,对于前i个字符,如果next[i]不等于零,那么说明在此字符串的前缀中,有一部分[0,next[i]]和本字符串[i-next[i],i]的这一部分是相同的。如果这i个字符组成一个周期串,那么错开的一部分[next[i],i]恰好是一个循环节。(换句话说,如果满足next[i]不等于零 && [next[i],i]是循环节这两点,就可以说明前i个字符组成一个周期串),那么我们只需要跑一遍next数组即可,代码:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 int n;
 5 string str;
 6 int nxt[1000005];
 7 
 8 void getnext(){
 9     int i = 0, j = -1, len = str.size();
10     nxt[0] = -1;
11     while(i < len){
12         if(j == -1 || str[i] == str[j])
13             nxt[++i] = ++j;
14         else
15             j = nxt[j];
16     }
17 }
18 
19 int main(){
20     ios_base::sync_with_stdio(false),cout.tie(0),cin.tie(0);
21     int tot = 1;
22     while(cin>>n && n){
23         cin>>str;
24         getnext();
25         cout << "Test case #" << tot++ << endl;
26         for(int i = 2; i <= n; i++){
27             if(nxt[i] != 0 && i % (i - nxt[i]) == 0)
28                 cout << i << " " << i/(i - nxt[i]) << endl;
29         }
30         cout << endl;
31     }
32 
33     return 0;
34 }

 

关于KMP算法就讲到这里了,这是一个很简单的串匹配算法,但能否掌握其思想以及运用其next数组,就得靠自己不断的磨练了。

posted @ 2019-01-07 17:23 Xenny 阅读(...) 评论(...) 编辑 收藏