KMP算法的时间复杂度分析

KMP算法的实现在网站上随处可见。我进行也进行了练习。但是对于其时间复杂的详细说明却少见。而且多数说的很是抽象。所以今天我便写下这篇博文,来梳理一下其时间复杂度。
1. 首先,贴出代码,此处使用的是C语言编写
#include <stdio.h>
#include <string.h>
void getNext(char* aim, int* next);
int getIndex(char *base, char *aim); 
int main (){
	char base [] = "afunction";
	char *aim = "function";
	int next[strlen(aim)];
	int index = getIndex(base, aim);
	printf("%d", index);
	return 0;
}
int getIndex(char *base, char *aim){
	int a = strlen(base);
	int b = strlen(aim);
	if (a < b) {
		return -1;
	}
	int i=0,j=0;
	int next[b];
	getNext(aim, next);
	while (i < a && j < b){
		if (base[i] == aim[j]) {
			i++;
			j++;
		}else if (j > 0){
			j = next[j-1] + 1;
		}else {
			i++;
		}
    }
    return j == b? (i - b) : - 1; 
}
void getNext(char* aim, int* next){
	int a = strlen(aim);
	next[0] = -1;
	int j = 0; 
	int i;
	for (i = 1; i < a; i++) {
		j = next[i-1];
		while(j > 0 & aim[i] != aim[j + 1]){//循环找到最大的符合条件的字从头开始的符串
			j = next[j];
		}
		if(aim[i] == aim[j + 1]){
			next[i] = next[i-1] + 1;
		}else {
		   next[i] = -1; 
		}
	}
}
  • 对于代码来说主要有两处的时间复杂度没有那么明显。

1)第一处

while (i < a && j < b){
		if (base[i] == aim[j]) { // i是base的索引,j是aim的索引
			i++;
			j++;
		}else if (j > 0){ //回滚
			j = next[j-1] + 1;
		}else {
			i++;
		}
    }

分析:

  • 乍一看去,对于每次循环,最多可以回滚j次;所有时间复杂度应该是
    O(ij);但是这种分析实在粗糙,没有考虑i,j之间的关联
  • 可以用一种更精细化的想法来刻画,该处的时间复杂度;这种分析是一种摊还分析。
    假设:base[i - 1] = aim[j - 1];且base[i] != aim[j];
    那么便有:
    对于base : [i - j -2, i -1] 处每个字符只判断过一次;
    对于base:i处字符串最多判断j次;
    所以有:
    base j + 1长度字符最多判断 j + j - 1 = 2j -1 次;
    那么对于base的这些字符长度的平均判断次数为:
    (2j-1)/(j+1); 它小于2;
    可以看出其中的i、j值都是变量;
    所以可以下这样的结论:
    base的字符的平均比较次数小于2;即base所以字符比较次数小于2a;
    这个算法的实际复杂度为O(a);

2)第二处:

for (i = 1; i < a; i++) {
		j = next[i-1];
		while(j > 0 & aim[i] != aim[j + 1]){//循环找到最大的符合条件的字从头开始的符串
			j = next[j];
		}
		if(aim[i] == aim[j + 1]){
			next[i] = next[i-1] + 1;
		}else {
		   next[i] = -1; 
		}
	}

分析:

  • 可以想象代码:
    while(j > 0 & aim[i] != aim[j + 1]){
    j = next[j];
    } 每循环一次都会将j值减少;最多减少为0;所以该回滚的最大次数为j;
  • 那么在前一次回滚最大次数的前提下,i+1索引处的回滚最多为几次呢?
    答案显然是0次
  • 我想你应该发现了相关性了,那就是对于回滚来说,它的最大全部回滚次数不会超过next[]增加的次数;你可以想象成一个栈;回滚就是取栈中元素;next[i] = next[i-1] + 1就是在栈中存一个元素;所有回滚之和(也就是while循环的总次数)不会超过next[i] = next[i-1] + 1语句执行的总次数
  • 而next[i] = next[i-1] + 1语句执行的总次数对多为a次
  • 综上,全部执行次数 < 2a;时间复杂度就是O(a)

总结:

  1. 由上,易得时间复杂度为O(a+b);其中a是base的长度、b是aim的长度。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值