字符串,模式匹配,KMP算法

时间:2023-01-08 06:12:39

  KMP算法,用于在一个字符串S中查找一个模式串P 的出现位置,算法复杂度为O(n+m)。

  当模式串P中的第j个字符跟字符串S中的第i个字符匹配失配时,i不回溯,模式串向右滑动最大K个字符位置,其第K+1的位置的字符与字符串S的第i个字符继续匹配,匹配了,i++,不匹配,模式串再向右滑动最大K个字符位置,依次类推。如何确定每次滑动的K的数值,这就是next数组。

  下图,模式串P的第j位与字符串S字符匹配失配的时候,可以看出,P第0位至第j-1位的字符串(与字符串S匹配的部分)的最大前缀字符串P的0~P的k-1与最大后缀字符串P的j-k~P的j-1相等( 前后缀从左向右对应相等,且有可能重叠部分字符,比如P[0~2] == P[2~4] ),与主字符串S无关。

  依据上述特性,可以计算出模式串第0~m-1,每个位置失配的时候的最大K(即模式串P向右滑动的最大值)放入next数组。

 

字符串,模式匹配,KMP算法

 

 1 #include <iostream>
 2 #include <string>
 3 #include <cassert>
 4 using namespace std;
 5 
 6 int KMPStrMatching(string S, string P, int *N, int start) 
 7 {
 8     int j= 0; // 模式的下标变量
 9     int i = start; // 目标的下标变量
10     int pLen = P.length( ); // 模式的长度
11     int tLen = S.length( ); // 目标的长度
12     if (tLen - start < pLen) // 若目标比模式短,匹配无法成功
13         return (-1);
14     while ( j < pLen && i < tLen) 
15     { // 反复比较,进行匹配
16         if ( j == -1 || S[i] == P[j])
17             i++, j++;
18         else j = N[j];
19     }
20     if (j >= pLen)
21         return (i-pLen); // 注意仔细算下标
22     else return (-1);
23 }
24 
25 int* findNext(string P) 
26 {
27     int j, k;
28     int m = P.length( ); // m为模式P的长度
29     assert( m > 0); // 若m=0,退出
30     int *next = new int[m]; // 动态存储区开辟整数数组
31     assert( next != 0); // 若开辟存储区域失败,退出
32     next[0] = -1;
33     j = 0; k = -1;
34     while (j < m-1) 
35     {
36         while (k >= 0 && P[k] != P[j])// 不等则采用 KMP 自找首尾子串
37             k = next[k]; // k 递归地向前找
38         j++; k++; next[j] = k;
39     }
40     return next;
41 }
42 
43 int main()
44 {
45     string s1 = "cocococola";
46     string s2 =   "cococola";
47     int ret = KMPStrMatching(s1,s2,findNext(s2),0);
48     cout << ret <<endl;
49     return 0;
50 }