C++(leetcode28)(记第一次内存泄漏)(BF|KMP匹配算法的理解和使用)

丫的,内存泄露了:

在申请内存的时候(使用了下面的那个,因此开辟出来的空间无法释放)

//申请空间的正确方式
int* next = new int[num + 2];//开辟数组

//申请数组空间的错误方式,会导致无法释放
int* next = new int(num + 2);

//释放空间的正确示例
delete []next;

题目简述:给出主串和字串+在主串中判断是否存在字串,

若son字符串是空,则返回0

若master是空或master中不存在son,则返回-1

法一:BF算法 ( Brute Force)

算法概述:BF(暴力)

一、使用两个指针分别指向两个串的开头

二、使用判断指针指向的字符是否匹配,匹配的话各下移一位

三、如果不匹配的话,主串的指针回到(此次开头的下一位),而字串回到开头

class Solution {
public:
    int strStr(string master, string son) {
        
	//先判断是否空串
	if (son.empty())
		return false;

	int pa = 1;
	int pb = 1;//master和son的指针
	//为了便于理解,使用的是由1开始,带入的时候需要处理

	while ((pb <= son.size()) && (pa <= master.size()))//子主均未未溢出
	{
		if (master[pa-1] == son[pb-1])
		{
			pa++;
			pb++;
		}
		else
		{
			pa = (pa - pb + 1)+1;
			//将pb当成步数的计数器,因为从1开始,因此计算的步数会多1
			//导致pa多退了一步加上一步,最后的+1意为再进一步
			pb = 1;
		}
	}
	if (pb > son.size())//子串完毕
		return pa-pb;
	else
		return -1;

    }
};

过程误区思考和总结:

一、string的下标从0开始

二、如果一个循环中,有一个数需要不断++,最后再回溯的话,有九成的把握可以当计数器

三、空间的数量的话,几乎没有叠加,因为就在原来的数组上加个指针而已,但是因为回溯的次数有点多,复杂度为O(mn)

 法二:KMP算法

KMP算法思想理解:

KMP算法的核心依然是匹配和右移,但是不同的是:

一、master的指针并不往回挪,而是挪动son字符串的方式

二、在算法初期就使用数组将son的每个字符若不匹配的情况下该回挪多少计算好

三、在以上的前提下,还有优化的空间:

        ①假若字符a与master中的b不匹配---->回溯到假如是a则必然与master的b不匹配则又要再次回溯,假如a的重复多次,则时间变长

        ②因此,可以使用nextval的数组配合所对应的【字符】二次处理next数组

如图所示:(假设字串为a b c a a b b c )

思想:如果回归后,相同,则取走你的值,若回归后不同则保留自己的值

手写笔记(流程|帮助理解)

思路探究:

通过上述的手写流程,可以明确的理解以下几个原则:

一、当两个指针所指的元素相同时,可以想象为:相同区变大(指针右移)(将此时可以匹配的i下标给k)

二、当指针元素不同时:

        ①根据已经有的返回值回溯,假如回溯后相等了,则认为可以续上(即k指针如果断开可以跳到这里|将i的点赋给k的位置)

        ②如果回溯后,发现仍然不等,则依旧回溯,回溯到0区(无人区)此时无元素可以匹配,因此无条件进一,并将该(指针k)处的回溯值定为i(此时必为1)

示意图

在这里插入图片描述

引用自:数据结构KMP算法配图详解(超详细)_哈顿之光的博客-CSDN博客_数据结构kmp算法详解

其实该图说明的就是:

依靠k寻找到j(最新断点的回溯位置)

class Solution {
public:
void GetNext(int next[], string son)//获取Next数组的函数
{
	int ne = 1;
	int dao = 0;
	//定义两个指针在不同数列
	next[1] = 0;//万年不变
	while (ne < son.size())//ne越界
	{
		if (dao == 0 || son[dao - 1] == son[ne - 1])
			//当dao退无可退的时候,必须就赋值1到对应位置
			//当其相等的时候,意味着红区拉长,将新红区的位置赋值过去
		{
			dao++;
			ne++;
			next[ne] = dao;
		}
		else
			//假如没有实现,则将dao回到最近匹配那里
			//思想是:保不住大区,看看小区行不行,如果实在不行,一直保到0区,那就从头开始
		{
			dao = next[dao];
		}
	}
	//能运行到这里就算完事了
}
int strStr(string master, string son)
{
	//先判断是否空串
	if (son.empty())
		return false;

	//获取next数组
	int num = son.size();
	int* next = new int[num + 2];//开辟数组-----加一用于适配从1开始
	int* tmp = next;
	if (next == NULL)
		return 0;
	GetNext(next, son);

	unsigned int pa = 1;
	unsigned int pb = 1;//master和son的指针
	//为了便于理解,使用的是由1开始,带入的时候需要处理

	while ((pb <= son.size()) && (pa <= master.size()))//子主均未未溢出
	{
		if (master[pa - 1] == son[pb - 1])//有可能会到size+1
		{
			pa++;
			pb++;
		}
		else
		{
			//pa = (pa - pb + 1)+1;   KMP算法不需要动pa
			//将pb当成步数的计数器,因为从1开始,因此计算的步数会多1
			//导致pa多退了一步加上一步,最后的+1意为再进一步
			if (pb == 1)
			{
				pa++;//因为字串实在没有找到开头能应对的,pa下一个
				continue;
			}
			pb = next[pb];//子串回溯
		}
	}
	delete []next;
	if (pb > son.size())//子串完毕
		return pa - pb ;
	else
		return -1;

}
};

总结:其实也不难,只要理解了KMP算法只是表明回溯的最好地方

在写next数组的时候,也是利用了回溯的思想,只是在生成next的时候,利用前面已经生成的next数,辅助后面的数字生成

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值