KMP 算法
阿新 • • 發佈:2017-10-08
計算 ++ 位置 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 算法