1. 程式人生 > >KMP 算法

KMP 算法

計算 ++ 位置 spa 提高效率 clas pre while esp

模板

如何在目標串中找到模式串p?

1、暴力枚舉起始位置,逐位比較

缺點:枚舉太多無用位置,時間復雜度高

Codes:

1 int j;
2 for(int i = 0;s[i];++ i){
3     for(j = 0;p[j];++ j)
4         if(s[i + j] != p[j]) 
5             break;
6     if(!p[j]){ //匹配成功 
7         //Operations
8     }
9 }

復雜度:O((n - m + 1) * m) (n:原串長度,m:匹配串長度)

2、KMP

優點:只枚舉了可能有用的位置(其實就是暴力缺點opp. QwQ)

那是如何做到只枚舉有用位置的呢?

nxt數組!

①nxt[i + 1] 定義為最大的 j + 1 使得 p[0~j] 是 p[0~i] 的後綴

技術分享

②nxt[i] 定義為當 i 位置不匹配時,將跳到 nxt[i] 位置重新匹配(關於nxt數組定義眾說紛紜,這裏只給出我認為最易懂的定義QwQ)

因為再不匹配時kmp算法並不是再枚舉下一位,而是跳到 nxt[i] 繼續匹配,所以會明顯提高效率,時間復雜度降到線性

例子1:

P : 0 1 2 3 4 5 6 7

a b c a b c a d

nxt: -1 0 0 0 1 2 3 4

例子2:

P : 0 1 2 3 4 5 6 7

a b a c b a b a

nxt: -1 0 0 1 0 0 1 2

清楚定義之後,計算nxt[]的思路也差不多了

Codes:

 1 void get_nxt(char *p){
 2     int i = 0,j = -1;
 3     nxt[0] = -1; //邊界 
 4     while(p[i]){
 5         if(j == -1 || p[j] == p[i]) //到達邊界 或 前綴等於後綴 
 6             nxt[++ i] = ++ j; //可以跳到的位置 + 1 
 7         else j = nxt[j]; //不匹配則跳到 nxt[] 
8 } 9 return; 10 }

接下來是匹配,可以觀察其與暴力的不同或相同點

Codes:

 1 int kmp(char *p,char *s){ //傳指針,可以理解為數組
 2     //s:目標串,p:模式串 
 3     int res = 0; //計數,出現次數 
 4     get_nxt(p); //處理nxt 
 5     for(int i = 0,j = 0;s[i];++ i){
 6         while(j != -1 && s[i] != p[j]) 
 7             j = nxt[j]; //若不匹配,跳到 nxt[j] 
 8         ++ j; //匹配上 
 9         if(!p[j]){ //模式串與目標串匹配完成 
10             res ++; //計數 ++ 
11             j = nxt[j]; //若計算重疊串則返回nxt[j] 
12             //Operations 
13         }
14     } 
15     return res;
16 }

模板(其實就是把上面搬過來2333)

Codes:

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<cstdio>
 5 #define N 1010
 6 
 7 using namespace std;
 8 
 9 char s[N],p[N];
10 int nxt[N];
11 void get_nxt(char *p){
12     int i = 0,j = -1;
13     nxt[0] = -1; //邊界 
14     while(p[i]){
15         if(j == -1 || p[j] == p[i]) //到達邊界 或 前綴等於後綴 
16             nxt[++ i] = ++ j; //可以跳到的位置 + 1 
17         else j = nxt[j]; //不匹配則跳到 nxt[] 
18     }
19     return;
20 }
21 int kmp(char *p,char *s){ //傳指針,可以理解為數組
22     //s:目標串,p:模式串 
23     int res = 0; //計數,出現次數 
24     get_nxt(p); //處理nxt 
25     for(int i = 0,j = 0;s[i];++ i){
26         while(j != -1 && s[i] != p[j]) 
27             j = nxt[j]; //若不匹配,跳到 nxt[j] 
28         ++ j; //匹配上 
29         if(!p[j]){ //模式串與目標串匹配完成 
30             res ++; //計數 ++ 
31             j = nxt[j]; //若計算重疊串則返回nxt[j] 
32             //Operations 
33         }
34     } 
35     return res;
36 }
37 int main(){
38     scanf("%s%s",s,p);
39     cout << kmp(s,p) << \n; 
40     return 0;
41 }

KMP 算法