【数据结构】KMP算法

简介

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)(资料来源百度百科)

next[i]数组

next[i]数组是什么

next[i]数组是一个用来储存前i个字符组成的字符串的前缀和后缀相同的个数的的函数

 在字符串”ABCABCACD"中,

截至为“ABCAB"除去我们所在的字符B剩下的串为

字符串"ABCA"中前缀和尾缀相同的仅为"A"

因此next[4]=1;

截至为“ABCABC"除去我们所在的字符C剩下的串为

 数组"ABCAB"中前缀和尾缀相同的为"AB"

因此next[5]=2;

怎么求next[i]数组

void getnext(int next[],string *S)
{
	int i = 0,j=-1;
	next[0] = -1;//仅是我们将其定义为next[0]为-1
	while (i < S->length)
	{
		if (j == -1 || S->str[i] == S->str[j])
		{
			i++;
			j++;
			next[i] = j;
		}
		else
			j = next[j];
	}
}

我刚开始看的时候对求next[i]的两句话不是很理解

为什么我们要有j==-1

if (j == -1 || S->str[i] == S->str[j])

 另一句是为啥是j=next[j] (重点)

j = next[j];

首先我们来讲第二句

在"ABCABCABD"中,当我们头缀尾缀匹配到B时,匹配上的已经有"ABCAB"

再接下去我们要进行匹配的是头缀的'C'和尾缀的'D',这时我们就已经匹配不上了

那此时进行j=next[j]即是令j=next[5]=2,仔细想想我们的'D'的前面的字符串有几个和头缀相同

就是next[5]个

所以我们的匹配就会接着把移到前面两个已经匹配了

如果我们的接下来的能够匹配上

假如:

 这样我们就可以接着匹配了

但要是没继续匹配上

 此时我们再去匹配会发现还是不匹配

所以再去看前面的头尾缀有相同的否就发现没有相同的,那么就会使得我们的匹配把j赋值为next[2]即是0

这时就是把'D'和首字符'A'进行比较

此时发现还是匹配不上那么就把j=next[0]=-1;

再回到

if (j == -1 || S->str[i] == S->str[j])

我们把这个这个拆开再去看就能够十分的明了了

		if (j == -1 )
		{
			i++;
			j++;
			next[i] = j;
		}
        else if ( S->str[i] == S->str[j])
		{
			i++;
			j++;
			next[i] = j;
		}

else if我们能够很明了的了解到

当两个相同的时候就可以移步到下一个这样他的去除自身的头尾缀最大就可以记录下来,接着包括他自身再次进行一个匹配,如果匹配上了则进入下个接着匹配,否则则进入else 进行j=next[j]的操作

那么j==-1

第一

就是为了我们最开始的时候在j=-1和i=0能够先进入j++和i++从而对字符串的第1个(S->str[0])和第2个(S->str[1])进行对比

第二是为了在我们在我们的当前字符(这里是'D')和首字符匹配不上的时候,那么就把当前字符的next[i]赋值为0,就是没有和前面有任何匹配,同时呢,再把我们要匹配的字符往下面再移动一个和第一个字符进行匹配

由于上面的几个的都是相同的表示所以我们就把j==-1和S->str[i] == S->str[j]合并起来了

KMP算法

我们先给出一段代码

int KMP(string* T, string* S)
{
	int i = 0, j = 0;
	int next[255];
	getnext(&next,S);
	while (i < T->length)
	{
		if (j == -1 || T->str[i] == S->str[j])
		{
			i++;
			j++;
			if (j == S->length)
				return i - j+1;
		}
		else
			j = next[j];
	}

}

首先

int i = 0, j = 0;

这边给j赋值为0就是为了最开始就进行S串和T串的匹配,如果匹配不上就把j赋值到-1,为了把我们要匹配的字符往下面再移动一个和第一个字符进行匹配(类同next的求法)


			if (j == S->length)
				return i - j+1;

用来返回最先匹配到的字符串的首的位置


		    j = next[j];

和next的求法里的意思是一样的当没匹配上的时候就会进行往前去寻找有没有匹配上的个数有则接着匹配,没有则回到最开头进行下个字符的匹配

最后附上全部的代码

#include<stdio.h>
#include<string.h>
typedef struct
{
	char str[255];
	int length;
}string;
int KMP(string* T, string* S);
void getnext(int next[],string *S);
int main()
{
	int n;
	string  T,S;
	gets(T.str);
	gets(S.str);
	T.length = strlen(T.str);
	S.length = strlen(S.str);
	n=KMP(&T,&S);
	printf("%d", n);
	return 0;
}
int KMP(string* T, string* S)
{
	int i = 0, j = 0;
	int next[255];
	getnext(next,S);
	while (i < T->length)
	{
		if (j == -1 || T->str[i] == S->str[j])
		{
			i++;
			j++;
			if (j == S->length)
				return i - j+1;
		}
		else
			j = next[j];
	}
 
}
void getnext(int next[],string *S)
{
	int i = 0,j=-1;
	next[0] = -1;
	while (i < S->length)
	{
		if (j == -1 || S->str[i] == S->str[j])
		{
			i++;
			j++;
			next[i] = j;
		}
		else
			j = next[j];
	}
}

  • 7
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值