欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

KMP算法

程序员文章站 2024-03-17 17:37:28
...

字符串匹配之KMP算法

假设文本是一个长度为n的字符串T,模板是一个长度为m的字符串P,且m<=n。需要求出模板在文本中的所有匹配点i,即满足T[i]=P[0],T[i+1]=P[1],…,T[m-1]=P[m-1]的非负整数(注意字符串下标从0开始)。KMP算法
最朴素的算法就是依此判断每个位置是不是一个匹配点。检查匹配点需要O(m)时间,而可能的匹配点有O(n-m)个,所以最坏的时间复杂度为O(m(n-1))。有一个简单优化:在检查匹配点的合法性时只要一个不同就立即停止匹配,换下一个匹配点。但最坏情况还是需要O(m(n-1))时间。
和朴素算法相比,KMP算法的时间效率就强多了。它首先用O(m)的时间对模板进行预处理,然后用O(n)的时间完成匹配。这样的算法复杂度已经是最好的了。
假定在匹配过程中正在比较文本串位置的字符和模板串abbaaba的最后一个字符,发现二者不同(称为失配),这时,朴素算法会把模板串向右移一位,重新比较abbaaba的第一个字符和文本串!!位置的字符。
KMP算法认为,既然!!位置已经比较过一次,就不应该再比一次了。事实上,我们已经知道灰色部分就是abbaab,应该可以利用模板串本身的特性判断出向右移一位一定不是匹配。同理右移两位或三位也不行,但是右移四位是有可能的。这个时候,需要比较
处的字符和abbaaba的第三个字符。
KMP算法
图片下方的那条链是一个状态机,其中编号为i的结点表示已经匹配了i个字符。匹配开始时当前状态是0,成功匹配状态加一(表示多匹配了一个字符),而失配时沿着“失配边”走。比如在这个例子中,如果在状态6时失配,应该转移到状态2。为方便起见,这里用next[i]表示状态i失配时应转移到的新状态,特别注意next[0]=0。
KMP算法不难写出:

void KMP(char* T,char* P,int* next){
	int n=strlen(T),m=strlen(P);
	getNext(P);
	int j=0;
	for(int i=0;i<n;i++){
		while(j&&P[j]!=T[i]) j=next[j];//顺着失配边走,直到可以匹配
		if(P[j]==T[i] j++;
		if(j==m) printf("%d\n",i-m+1);
		}
	}

接下来就到了KMP算法的关键,也是它最巧妙的地方。算法的思想是“用自己匹配自己”,根据next[0],next[1],…,next[i-1]递推next[i],上代码:

void getNext(char* P,char* next){
	int m=strlen(P);
	f[0]=0,f[1]=0;
	for(int i=1;i<m;i++){
		int j=next[i];
		while(j&&P[i]!=P[j] j=next[j];
		next[i+1]=P[i]==P[j]?j+1:0;
		}
	}

字符串ABRACADABRA的状态转移表如图所示:
KMP算法
到此KMP的基本算法就实现完成了。

相关标签: KMP算法

上一篇: KMP算法详解

下一篇: KMP算法