丫的,内存泄露了:
在申请内存的时候(使用了下面的那个,因此开辟出来的空间无法释放)
//申请空间的正确方式 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数,辅助后面的数字生成