学习KMP算法(详解)

KMP算法是一个广泛应用于字符串查找与匹配的算法,特点就是计算速度快,例如在m长度的字符串中查找匹配长度为n的字符串,他的时间复杂度可以是m+n

对于字符串的查找与匹配,要是我们没有学习过数据结构或者是算法,我们也能够想出来一个很传统的方法:那就是在主z串中一个一个字符推进,然后和要匹配的字符串一位一位字符比较。虽然这个方法也能完成任务,但是在复杂的的长字符串中,计算花费时间特别长,时间复杂度可以到m*n字符

传统暴力匹配算法:

int index(char *a,char *b) {
	int lena = strlen(a);
	int lenb = strlen(b);
	while(i < lena && j < lenb) {
		if(a[i] == b[j]) {
			++i;
            ++j;
		}
		else {
			j=1;
            i=++k;
		}
	}
	if(j >= lenb) return k;
	else return 0;//查找失败返回0
}
//在字符串a中查找b

为了优化计算,我们可以学习使用KMP算法来帮助我们解决查找匹配这一类的问题。

要理解KMP算法,我们首先要知道最大前后缀字符串的概念,下面图就对最大前后缀做的一个解释:用字符串 ABCADABCD 举例

c832efcad8574c1ab8e2dcd78119f9e0.jpg

由此我们可以引入一个next数组,来记录我们搜索到的最大前后缀,还是用上图的字符串为例我们可以最后一个字符的位置当做next的下标,最大前后缀为此时next的值,于是我们可以写出next(ABCADABCD):

next[0]=-1

next[1]=0

next[3]=0

next[6]=1

扫描二维码关注公众号,回复: 14600017 查看本文章

next[7]=2

next[8]=3

接下来,我们我们可以试着看看最大前后缀在匹配中的运用,我们在ABAABCADBABCADABCDBAD

中寻找

ABCADABCD

ada5eb7d65a4477dab28a8d368052c1b.jpg

 这就是一次KMP算法查找匹配的操作,代码的具体实现是这样的。第一步,我们要先计算next,然后是按照思路去匹配

//在字符串a中查找字符串b
int KMP(char *a, char *b) {
    int lena = strlen(a);
    int lenb = strlen(b);
    int now = 0, k = -1;
    next[0] = -1;
    while(now < lenb) {
        if(k == -1 || b[now] == b[k]) {
            now++;
            k++;
            next[now] = k;

        }
        else k = next[k];
//这个位置的回溯是一个类似递归的思想,我们不是一步就回到字符串的开头,而是如果现有的最大前后缀内还有最大前后缀,可以回溯到这个位置继续判断,是一个优化的思想
    }
//先求b串的next[]
    int i = 0, j = 0;
    while(i < lena && j < lenb) {
        if(j == -1 || a[i] == b[j]) {
            i++;
            j++;
        }
        else j = next[j];
    }
    if(j >= lenb) return (i - lenb);//找到了就返回匹配到的起点位置
    else return (-1);//没到到返回-1
}

但是这个依旧不是最优的,我们在算next的时候,没有考虑连续重复字符串的情况,例如在AAACBAAAB中寻找AAAB这个字符串的时候

cc6dbafc0b1e4763bd97e0ec230bffce.jpg

void nextval(char *s, int nextval[]) {
    int len = strlen(s);
    int now = 0, k = -1;
    nextval[0] = -1;
    while(now < len) {
        if(k == -1 || s[now] == s[k]) {
             now++;
             k++;
             if(s[now] != s[j]) nextval[now] = k;//这个位置多了一个判断
             else nextval[now] = nextval[k];
        }
        else k = nextval[k];    
    }
}

总结:

KMP算法是一个很巧妙的算法,利用前后缀的思想,跳过了繁琐的步骤。同时仅仅通过一个字符串长度的空间,却换来了极大的速度优化,这也是一个用空间换时间的典型案例。

猜你喜欢

转载自blog.csdn.net/lijj0304/article/details/127265059
今日推荐