字符串查找-普通方法和KMP(普拉特操作)介绍

普通的字符串匹配方法(串的模式匹配方法)

对于普通的串匹配方法,通过简单的例子进行解析

T: a b a c a a b a c a b a c

W: a b a c a b

会从T[0]W[0]进行匹配,如果相等则匹配W的下一个字符,直到出现不相等的情况。简单丢弃W[0]开始的匹配信息,然后从T[1]开始继续同W进行匹配,直到串结束或者满足W长度和T长度差值的循环,如果T中不够nW自然可以结束。

 

具体实例如下所示,返回第一个匹配串及其后边的字符,时间复杂度O((m-n)*n)

const char* strFind(const char* string1, const char* substring)
{
	assert(string1 != NULL && substring != NULL);

	int m = strlen(string1);
	int n = strlen(substring);

	if (m < n)
		return NULL;
	
	for (int i = 0; i <= m - n; ++i)
	{
		int j = 0;
		for (; j < n; ++j)//对substring循环,也就是上文中的W
		{
			if (string1[i + j] != substring[j])
				break;
		}

		if (j == n)
			return (string1 + i);
	}

	return NULL;
}

这种简单的丢弃前面的信息造成了极大的浪费和低下的匹配效率。

KMP算法是由D.EV.R同时发现,当前算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体就是实现next()函数,函数本身包含了模式串的局部匹配信息。

针对第一种基本匹配方法,KMP方法对于每一个模式串会事先计算出模式串内部匹配信息,在匹配失败时最大移动模式串,以减少匹配次数。

如何计算右移距离成了算法的核心,右移距离的计算也就是next的计算,也就是输入串前缀后缀对应的匹配值的计算。

什么是前后缀?

 “前缀”指除了最后一个字符以外,一个字符串的全部头部组合,“后缀”除了第一个字符以外,一个字符串的全部尾部组合。实例如下:

字符串:bread

前缀:b, br, bre, brea

后缀:read, ead, ad, d

部分匹配值的计算:

搜索词:A B C D A B D

部分匹配值就是“前缀”和“后缀”的最长共有元素的长度。根据以上搜索词为例:

A的前缀和后缀都为空集,共有元素的长度为零。

AB的前缀为A,后缀为B,共有元素长度为0

ABC前缀为A, AB,后缀为BCC共有元素长度为0.

ABCD前缀为AABABC,后缀为BCDCDD,共有元素长度为0

ABCDA前缀为AABABCABCD,后缀为BCDACDADAA共有元素为A,共有元素长度为1

ABCDAB前缀AABABCABCDABCDA,后缀为BCDABCDABDABABB。共有元素为AB,长度为2.

部分匹配的实质是:有时候字符串头部和尾部会有重复,例如“ABCDAB”之中有两个“AB”,那么他的部分匹配值就是2(“AB”的长度)。搜索移动的时候第一个AB向后移动思维(字符串长度减去部分匹配值),第一个AB向后移动四位就可以到达尾部AB的位置。

得到:移动位数 = 字符串长度  部分匹配值。字符串长度和部分匹配值的求解分别都是针对已经匹配的模式串子串进行的求解,实例如下:

T: a b a c a a b a c a b a c

W: a b a c a b

T[5]W[5]出现不匹配,T[0]T[4]是匹配的。字符串长度为5

前缀:aa ba b aa b a c; 后缀:b a c aa c ac aa; 共有元素为a,长度为1

因此可求的移动位数为4

Next求解代码:

void get_nextval(char const* ptrn, int plen, int* nextval)
{
	int i = 0;
	nextval[i] = -1;
	int j = -1;

	while(i < plen - 1)
	{
		if (j == -1 || ptrn[i]==ptrn[j])
		{
			++i;
			++j;
			nextval[i] = j;
		} 
		else
		{
			j = nextval[j];
		}
	}
}

字符串查找实例如下所示:

T: a b a c a a b a c a b a c a b a a b b

W: a b a c a b

 

可求得Wnext表为:

W

a

b

a

c

a

b

 

0

0

1

0

1

2

关于next求解,加入第二项的a,其实就是求的aba的前缀后缀公共项的最大值。

 

a b a c a a b a c a b a c a b a a b b

a b a c a b

从第0项开始对比,直到找到不相等的项:发现T[5]W[5]不相等,也就是说T[0]-T[4]相等,获取T[0]-T[4]的前缀后缀相同项,最大长度,查表可得为1.

移动位数 = 5  1 = 4;

a b a c a a b a c a b a c a b a a b b

a b a c a b

 

按照此规律反复执行。时间复杂度O(m+n)

代码如下:

int kmp_search(const char*src,int slen,const char* patn, int plen,const int* nextval, int pos)
{
	int i = pos, j = 0;

	while(i<slen && j<plen)
	{
		if (j == -1 || src[i] == patn[j])
		{
			++i;
			++j;
		}else
		{
			j = nextval[j];
		}
	}

	if (j >= plen)
	{
		return i-plen;
	} 
	else
	{
		return -1;
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我! 毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值