一、实现strStr函数
1.题目
Leetcode:第 28 题
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。
如果 needle 不是 haystack 的一部分,则返回 -1 。
示例 1:
输入:haystack = "sadbutsad", needle = "sad"
输出:0
解释:"sad" 在下标 0 和 6 处匹配。
第一个匹配项的下标是 0 ,所以返回 0 。
示例 2:
输入:haystack = "leetcode", needle = "leeto"
输出:-1
解释:"leeto" 没有在 "leetcode" 中出现,所以返回 -1
2.解题思路
使用KMP算法,先计算next数组,该数组用于在字符串匹配中跳过已经知道的不匹配前缀,
再根据next数组遍历模式串和字符串,直到找到完全匹配的字符串,并返回第一个匹配项的下标。
3.实现代码
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Solution
{
public:
// getNext函数用于计算KMP算法的next数组,
// 该数组用于在字符串匹配中跳过已经知道的不匹配前缀
void getNext(vector<int>&next, const string& s)
{
int j = 0; // 初始化j为0,表示当前没有匹配的前缀和后缀
next[0] = 0; // 对于模式串的第一个字符,next数组的第一个值设为0
for (int i = 1; i < s.size(); i++) // 从模式串的第二个字符开始遍历
{
// 当前缀和后缀不匹配时,根据next数组回溯到上一个匹配的位置
while (j > 0 && s[i] != s[j])
{
j = next[j - 1];
}
// 如果前缀和后缀匹配,j向前移动到下一个位置
if (s[i] == s[j])
{
j++;
}
next[i] = j; // 记录当前位置的前缀和后缀的最长匹配长度
}
}
// strStr函数使用KMP算法在字符串haystack中查找子字符串needle的起始索引
int strStr(string haystack, string needle)
{
// 如果needle为空字符串,直接返回0,表示找到了匹配(空字符串可以在任何位置开始)
if (needle.size() == 0)
{
return 0;
}
vector<int> next(needle.size());// 分配next数组,用于存储needle的next值
getNext(next, needle); // 计算needle的next数组
int j = 0; // 初始化j为0,表示当前没有匹配的前缀和后缀
for (int i = 0; i < haystack.size(); i++) // 遍历haystack中的每个字符
{
// 当haystack中的字符与needle中的前缀和后缀不匹配时,根据next数组回溯
while (j > 0 && haystack[i] != needle[j])
{
j = next[j - 1];
}
// 如果当前字符匹配,j向前移动到下一个位置
if (haystack[i] == needle[j])
{
j++;
}
// 如果j等于needle的长度,表示找到了匹配
if (j == needle.size())
{
// 返回匹配的起始索引,即i减去needle的长度再加1
return (i - needle.size() + 1);
}
}
// 如果遍历完haystack后没有找到匹配,返回-1
return -1;
}
};
//测试
int main()
{
Solution s;
string haystack = "abcsadbutsad";
string needle = "sad";
cout << "haystack = " << haystack << endl;
cout << "needle = " << needle << endl;
cout <<"在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标:"<<s.strStr(haystack,needle) << endl;
cout <<endl;
return 0;
}
二、重复的子字符串
1.题目
Leetcode:第 459 题
给定一个非空的字符串s ,检查是否可以通过由它的一个子串重复多次构成。
示例 1:
输入: s = "abab"
输出: true
解释: 可由子串 "ab" 重复两次构成。
示例 2:
输入: s = "aba"
输出: false
示例 3:
输入: s = "abcabcabcabc"
输出: true
解释: 可由子串 "abc" 重复四次构成。 (或子串 "abcabc" 重复两次构成。)
2.解题思路
使用KMP算法,先计算next数组,检查next数组的最后一个值不为0,则说明字符串有最长相同的前后缀,并且当s的长度可以被(s的长度 - 最长相等前后缀的长度), 即第一个重复长度整除时,说明字符串可以由子串重复多次构成。
3.实现代码
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
// getNext函数用于计算字符串s的next数组,该数组用于KMP算法中的部分匹配表
void getNext(vector<int>&next, const string& s)
{
int j = 0; // 初始化j为0,表示当前没有匹配的前缀和后缀
next[0] = 0; // 对于字符串的第一个字符,next数组的第一个值设为0
for (int i = 1; i < s.size(); i++) // 从字符串的第二个字符开始遍历
{
while (j > 0 && s[i] != s[j])
{
j = next[j - 1];// 当前缀和后缀不匹配时,根据next数组回溯
}
if (s[i] == s[j])
{
j++;// 如果前缀和后缀匹配,j向前移动到下一个位置
}
next[i] = j; // 记录当前位置的前缀和后缀的最长匹配长度
}
}
// repeatedSubstringPattern函数用于检查字符串s是否可以由其某个子字符串重复构成
bool repeatedSubstringPattern(string s)
{
if (s.size() == 0) // 如果字符串为空,直接返回false
{
return false;
}
vector<int> next(s.size()); // 创建next数组,用于存储计算结果
getNext(next, s); // 调用getNext函数计算next数组
int len = s.size(); // 获取字符串s的长度
// 检查next数组的最后一个值不为0,则说明字符串有最长相同的前后缀
// 并且s的长度可以被(s的长度-最长相等前后缀的长度),即第一个重复长度,整除
if (next[len - 1] != 0 && len % (len - (next[len - 1])) == 0)
{
return true; // 如果满足条件,说明s可以由其某个子字符串重复构成
}
return false; // 否则,s不能由其某个子字符串重复构成
}
};
//测试
int main()
{
Solution p;
string s = "abab";
cout << "s = " <<s<< endl;
if (p.repeatedSubstringPattern(s))
{
cout <<s<<"可以由它的一个子串重复多次构成。" << endl;
}
else
{
cout << s << "不可以由它的一个子串重复多次构成。" << endl;
}
cout <<endl;
return 0;
}
ps:以上皆是本人在探索算法世界旅途中的浅薄见解,诚挚地希望得到各位的宝贵意见与悉心指导,若有不足或谬误之处,还请多多指教。
标签:匹配,day9,needle,next,算法,数组,字符串,haystack,Leetcode From: https://blog.csdn.net/m0_74882777/article/details/137184744