时间复杂度:o(m+n)
特点:每当一趟匹配过程中出现字符比较不等时,不需回溯主串迭代器,而是利用已经得到的“部分匹配”的结果将模式串向右“滑动”尽可能远的一段距离后(即移动模式串迭代器)继续比较。
相关概念:
算法精髓:主串与模式串匹配过程中比较模式串中的第k个字符不等时,模式串迭代器重新指向模式串前k个字符的最长相等前缀的后一个字符,再继续比较。
这是因为当主串中的某个字符与模式串中的第k个字符不等时,包括该字符在内的前k个字符与模式串中的前k个字符拥有相同的最长相等前后缀。因为该字符的前k-1个字符是和模式串的前k-1个字符完全相等的。
应用实例:在主串“abababc”中查找“ababc”
我们已经知道了KMP算法就是在比较过程中不断地移动模式串指针到最长相等前缀后一个字符。但还有一些细节需要我们处理:
代码:
typedef struct str {
char* val;
int length;
}String;
int* getNext(String sub);
//使用KMP算法得到模式串的索引
//str:主串
//sub:模式串
//返回:找不到返回-1,否则返回首字符在主串中的索引
int indexOf_kmp(String str, String sub) {
int i = 0, j = 0;//i,j分别迭代主串、模式串
int* next = getNext(sub);//next数组存储模式串所有子串的最长相等前后缀的长度
while (i < str.length-1 && j < sub.length-1) {
if (j == -1 || str.val[i] == sub.val[j]) {
i++; j++;
}
else {
j = next[j];//next[j]存储着前j个字符的最长相等前后缀的长度,而最长相等前缀后一个字符的索引正好等于该长度
}
}
free(next);
if (i == str.length)
return -1;
return i - 1 - sub.length;
}
求解next数组:
算法精髓:两个迭代器初始时刻一个指向第一个字符,一个指向第二个字符,求next[j]只需比较两个迭代器指向的字符,如果相等则next[j]=next[i]+1,再分别增加两个迭代器。如果不相等,则令第一个迭代器指向第next[i]个字符,再进行比较,相等则next[j]=next[next[i]]+1。如果不相等,则继续令第一个迭代器指向第next[next[i]]个字符……直到相等再分别增加两个迭代器。(其实就是反复使用KMP算法精髓)
在求解之前,我们先来规定一下一些特殊情况:
算法原理:
若一个椭圆代表一个字符,假设两个迭代器指向的字符相等,则next[j]=next[i]+1
假设两个迭代器指向的字符不等,且next[i]=7,则i=7
如果还不相等,假设next[7]=3,则i=3
如果相等了,则第一个蓝框和最后一个蓝框向右扩展一格,即next[j]=next[next[i]]+1。如果不相等则继续让i=next[i],直到i=-1或者相等。
代码:
int* getNext(String sub) {
int* next = (int*)malloc(sizeof(int)*sub.length);
next[0] = -1;
int i = 0, j = 1;
while (j < sub.length) {
if (i == -1) {
i = 0;
next[j] = 0;
j++;
}
else if (sub.val[i] == sub.val[j]) {
next[j] = i + 1;
i++;
j++;
}
else {
i = next[i];
}
}
return next;
}
可以发现存在一些冗余代码,我们优化一下:
int* getNext(String sub) {
int* next = (int*)malloc(sizeof(int)*sub.length);
next[0] = -1;
int i = 0, j = 1;
while (j < sub.length || sub.val[i] == sub.val[j]) {
if (i == -1) {
next[j++] = ++i;
}
else {
i = next[i];
}
}
return next;
}
大功告成!
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- huatuoyibo.net 版权所有 湘ICP备2023021910号-2
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务