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数组。
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 }