31.找出字符串中第一个匹配项的下标
(力扣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 。
提示:
-
1 <= haystack.length, needle.length <= 104
-
haystack
和needle
仅由小写英文字符组成 -
解题思路:
- 构造
next
数组:- 使用
getNext
函数生成needle
的部分匹配表(next
数组)。next[i]
表示needle
的前i
个字符中,最长相同前缀和后缀的长度。 - 通过双指针
i
和j
,i
遍历字符串,j
表示当前匹配的前缀长度。如果当前字符不匹配,则通过next[j - 1]
回退,直到找到匹配的前缀或回退到0。
- 使用
- 匹配过程:
- 在
strStr
函数中,使用next
数组辅助匹配。 - 遍历
haystack
,用指针j
记录当前匹配到needle
的位置。 - 如果当前字符不匹配且
j > 0
,则通过next[j - 1]
回退。 - 如果匹配成功,
j
增加;如果j
达到needle.size()
,说明找到完整匹配,返回起始位置。 - 如果遍历完
haystack
仍未找到匹配,返回-1
。
- 在
关键点:
next
数组的生成是 KMP 算法的核心,它帮助快速跳过已匹配的部分,避免重复比较。- 匹配过程中利用
next
数组实现高效回退,减少不必要的字符比较。
class Solution
{
public:
void getNext(int *next, const string &s)
{
// 初始化
int j = 0; // j指向前缀末尾位置
next[0] = 0; // 构造next数组,前缀表
for (int i = 1; i < s.size(); i++) // ,i指向后缀末尾位置
{
// 前后缀不同
while (j > 0 && s[i] != s[j])
{
j = next[j - 1 ];// 向前回退
}
// 处理前后缀相同的情况
if (s[i] == s[j])
{
j++;
}
// // 将j(前缀的长度)赋给next[i]
next[i] = j;
}
}
int strStr(string haystack, string needle)
{
// needle为空
if (needle.size() == 0)
{
return 0;
}
// 初始化 next 数组,用于存储 needle 的部分匹配表
vector<int> next(needle.size());
// / 构造 next 数组
getNext(&next[0], needle);
// j 用于记录当前匹配到 needle 的位置
int j = 0;
// // 遍历 haystack
for (int i = 0; i < haystack.size(); ++i)
{
// // 如果当前字符不匹配,并且 j > 0,说明可以回退
while (j > 0 && haystack[i] != needle[j])
{
// 回退到 next[j-1] 的位置
j = next[j - 1]; // 向前回退
}
// 如果当前字符匹配
if (haystack[i] == needle[j])
{
j++;
}
// 如果 j 达到 needle 的长度,说明找到了完整的 needle
if (j == needle.size())
{
// 返回 needle 在 haystack 中的起始位置
return (i - needle.size() + 1);
}
}
// 如果遍历完 haystack 也没有找到 needle,返回 -1
return -1;
}
};
- 时间复杂度: O(n + m)
- 空间复杂度: O(m)
32.右旋字符串
(卡码网55题)
题目描述
字符串的右旋转操作是把字符串尾部的若干个字符转移到字符串的前面。给定一个字符串 s 和一个正整数 k,请编写一个函数,将字符串中的后面 k 个字符移到字符串的前面,实现字符串的右旋转操作。
例如,对于输入字符串 “abcdefg
” 和整数 2,函数应该将其转换为 “fgabcde
”。
输入:输入共包含两行,第一行为一个正整数 k,代表右旋转的位数。第二行为字符串 s,代表需要旋转的字符串。
输出:输出共一行,为进行了右旋转操作后的字符串。
样例输入:
2
abcdefg
样例输出:
fgabcde
数据范围:1 <= k < 10000, 1 <= s.length
< 10000;
代码
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
int n = 0;
string s = "";
cin >> n;
cin >> s;
// 整体反转
reverse(s.begin(), s.end());
// 前一段反转
reverse(s.begin(), s.begin() + n);
// 后一段反转
reverse( s.begin() + n, s.end());
cout << s << endl;
return 0;
}
- 时间复杂度: O(
len
)len
是字符串的长度 - 空间复杂度: O(1)