数据结构中讲到关于字符串匹配算法时,提到朴素匹配算法,和KMP匹配算法。
朴素匹配算法就是简单的一个一个匹配字符,如果遇到不匹配字符那么就在源字符串中迭代下一个位置一个一个的匹配,这样计算起来会有很多多余的不符合的匹配做了冗余的比较。假设源字符串长n,字串长m 该算法最差时间复杂度为 m*(n-m+1),记为O(n*m);这里不做过多解释朴素匹配算法。
KMP算法:
kmp算法不是在源字符串中下手,他是从字串下手,比如我要在源字符串(acabaabaabcacaabc)中匹配一个字符串字串(abaabcac),那么从字串abaabcac下手,分析字串时,需要借助于一个数组存储字串中存在头字串和尾字串对称相等的子串长度,例如 abaabcac,
a next[0] = -1,规定第一个字符对应的next值为-1;
ab next[1] = 0; 因为针对字符b而言,其前边字符串a 不存在头字串和尾字串对称,所以为0;
aba next[2]=0 ; 因为针对子串 ab ,不存在头字串和尾字串对称,所以为0;
abaa next[3]=1 ; 因为针对子串aba ,存在 头子串a和尾子串a对称相等,其长度为1,所以为1;
abaab next[4]=1; 因为针对子串abaa ,存在 头子串a和尾子串a对称相等,其长度为1,所以为1;
abaabc next[5]=2; 因为针对子串abaab ,存在 头子串ab和尾子串ab对称相等,其长度为2,所以为2;
abaabca next[5]=0; 因为针对子串abaab ,,不存在头字串和尾字串对称,所以为0;
abaabcac next[6]=1; 因为针对子串abaabca,存在 头子串a和尾子串a对称相等,其长度为1,所以为1;
总结起来如下:
J 0 1 2 3 4 5 6 7 P a b a a b c a c next(j) -1 0 0 1 1 2 0 1
获取next数组的代码如下
//获取模式匹配字符串的next数组 void getNext(char *str,char *next) { int j = 0; int k = -1; int length = strlen(str); next[0] = -1; while(j<length) { if(k == -1 || str[j] == str[k]) { j++; k++; next[j] = k; }else k = next[k]; } }
然后在匹配的过程中,如果遇到不匹配现象时,从不匹配位置分析,其next[i]的值标记着有n个头子串和尾子串相等,即直接从next[i]的值为下标开始寻找匹配。复杂度为O(m+n) KMP实现代码:
//src为要匹配的字符串,pat为字符串模型 int KMP(char *src,char *pat) { char next[100]; getNext(pat,next); int lengthP = strlen(pat); int lengthS = strlen(src); int posS=0,posP=-1; bool flag = false; while(posS < lengthS && posP < lengthP) { if (posP==-1 ||src[posS] == pat[posP]) { if (flag) posS++; posP++; }else { posP = next[posP]; flag = true; } } if (posP<lengthP)return -1; else return posS-lengthP; }
完整的代码:
#include<stdio.h> #include<string.h> //获取模式匹配字符串的next数组 void getNext(char *str,char *next) { int j = 0; int k = -1; int length = strlen(str); next[0] = -1; while(j<length) { if(k == -1 || str[j] == str[k]) { j++; k++; next[j] = k; }else k = next[k]; } } //src为要匹配的字符串,pat为字符串模型 int KMP(char *src,char *pat) { char next[100]; getNext(pat,next); int lengthP = strlen(pat); int lengthS = strlen(src); int posS=0,posP=-1; bool flag = false; while(posS < lengthS && posP < lengthP) { if (posP==-1 ||src[posS] == pat[posP]) { if (flag) posS++; posP++; }else { posP = next[posP]; flag = true; } } if (posP<lengthP)return -1; else return posS-lengthP; } int main() { char src[100]; char pat[50]; printf("请输入要匹配的字符串和字符串模板(字串):\n"); scanf("%s%s",src,pat); int f = KMP(src,pat); printf("在元字符串中匹配位置的下标为 %d ",f); return 0; }