KMP算法

KMP算法:KMP是由3个外国人想出来的设计的线性时间字符串匹配算法。时间复杂度很低O(n),是判定字串的一个十分简便的方法。KMP算法在搜索发生失配时,将模式串向右滑动到某个位置重新开始匹配而不是回溯,提高了搜索效率。经过分析,KMP算法的时间复杂度为O(m+n),暴力搜索算法的时间复杂度为O(mn),KMP算法的搜索效率远远高于暴力搜索算法。

我们采用了一个next数组来解决该问题。

KMP算法的关键所在就是求出字符串的next数组。

方法:

next = 前后缀最长公共部分+1
因为最长公共部分是已经匹配了的,所以要从公共部分的下一个开始比较,而next的值就是下一次比较时开始的位置.
这里需要注意两点,
一.待匹配串的第一个字符是不需要回退的,因为它就是回退的最后一个了.
二.子串前后缀没有重合部分且子串不为空,即需要从子串头开始匹配,所以next=1.

a  b  a  b  a  b  b

对于这个字符串,来对它进行求next

当j=1时发生不匹配,子串为空,next = 0
当j=2时发生不匹配,子串为 A,前后缀公共部分是空, next = 1
当j=3时发生不匹配,子串为 AB,前后缀公共部分为空, next = 1,
当j=4时发生不匹配,子串为 ABA,前后缀公共部分是A, next = 2
当j=5时发生不匹配,子串为 ABAB,前后缀公共部分是AB, next = 3
当j=6时发生不匹配,子串为 ABABA,前后缀公共部分是ABA, next = 4
当j=7时发生不匹配,子串为 ABABAB,前后缀公共部分是ABAB, next = 5

最终得到next数组的值

next数组如何求出依靠这个公式

//求出next数组
void getNext(char arr[], int next[])
{
	int i = 1;
	int j = 0;
	next[1] = 0;

	while (i < (int)strlen(arr))
	{
		if (j == 0 || arr[i] == arr[j])
		{
			i++;
			j++;
			next[i] = j;
		}
		else
		{
			j = next[j];
		}
	}
}

kmp算法的改进:

上述根据next数组去查询子串还是有缺陷的,如果主串为“aaaabcde”,子串为“aaaaax”的话,子串的next数组值为“012345”在开始匹配时发现当i=5,j=5,时b与a不相等。

因此j=next[5]=4,如图。

此时a与b 仍不相等。j=next[4]=3;如图

一直到j=0时,根据算法重新算出i=6,j=1.如图

去进行下次匹配,我们发现效率还是比较低。中间还有其他多余的判断。于是我们对算法进行了改进。

给出 一个字符串S“abababcab'',和子串“ababc”.

现在我们重新定义一个新的next数组,把子串的最小前后缀的数字存放到next数组中。

子串“ababc”的子串有a,ab,aba,abab.四个。

子串“ababc”的前缀有四个a,ab,aba,abab.后缀也有四个b,ba,bab,babc.

接下来我们求出子字符串的最长前后缀。

a 没有最长前后缀,为0.

ab,前缀为a,后缀为b。前后缀不相等。为0.

aba,前缀为a,ab后缀为a,ba。最小前缀相等。为1

abab,前缀为a,ab,aba.后缀为b,ab,bab。ab相等。为2

将这四个数字组成一个数组,数组的第一位是-1,这样就构成了新的next数组{-1,0,0,1,2}。

接下来我们在字符串S中进行查找子串。

当匹配到第四个数字时发现不相等,这时候C处的next数组值为2.

那么将下面的字符串重新匹配将数组下标为2的位置重新放到星星标记的位置。

 

这样就匹配成功了。

#include<iostream>
using namespace std;

//next数组
void Get_Next(char* T, int *next)
{
	int j = 0;
	int k = -1;
	next[0] = -1;
	while (j < strlen(T))
	{
		if (k == -1 || T[j] == T[k])
		{
			j++;
			k++;
			next[j] = k;
		}
		else
		{
			k = next[k];
		}
	}
}

int KMP(char* S, char* T, int *next)
{
	int i = 0, j = 0;
	Get_Next(T, next);
	int slen = strlen(S);
	int tlen = strlen(T);

	while (i < slen && j < tlen)
	{
		if (j == -1 || S[i] == T[j])
		{
			i++;
			j++;
		}
		else
		{
			j = next[j];
		}
	}
	if (j == tlen)
	{
		return i - tlen;
	}
	else
	{
		return -1;
	}
}

int main()
{
	cout << "请输入一串字符和要查找的字符串:" << endl;
	char S[32];
	char T[32];
	cin >> S >> T;
	int next[32];

	Get_Next(T, next);
	cout << "next数组为:";
	for (int i = 0; i < strlen(T); i++)
	{
	printf("%d ", next[i]);
	}

	cout << endl;
	cout << "匹配出现的位置:" << KMP(S, T, next) << endl;

	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值