LeetCode344. 反转字符串
https://leetcode-cn.com/problems/reverse-string/
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[]
的形式给出
输入:["h","e","l","l","o"]
输出:["o","l","l","e","h"]
思路
使用双指针法。定义一个left在下标0的位置上,定义right在下标sz - 1的位置上,交换两者;left向右移动同时right向左移动,交换两者,直到left == right结束。
本题思路简单,主要为后续题目字符串操作提供一个基础
代码
class Solution
{
public:
void reverseString(vector<char>& s)
{
int sz = s.size();
int left = 0, right = sz - 1;
while (left < right)
{
swap(s[left], s[right]);
++left;
--right;
}
}
};
LeetCode541. 反转字符串II
https://leetcode-cn.com/problems/reverse-string-ii/
给定一个字符串 s 和一个整数 k,你需要对从字符串开头算起的每隔 2k 个字符的前 k 个字符进行反转。
如果剩余字符少于 k 个,则将剩余字符全部反转。
如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
输入: s = "abcdefg", k = 2
输出: "bacdfeg"
思路
本题就是模拟,实现题目中规定的反转规则就可以了。具体操作见代码和注释。
代码
class Solution
{
private:
void reverse(string& s, int left, int right)
{
while (left < right)
{
swap(s[left], s[right]);
++left;
--right;
}
}
public:
string reverseStr(string s, int k)
{
int sz = s.size();
for (int i = 0; i < sz; i += 2 * k)
{
//每隔 2k 个字符的前 k 个字符进行反转
//剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
if (i + k - 1 <= sz - 1)
{
reverse(s, i, i + k - 1);
}
//剩余字符少于 k 个,则将剩余字符全部反转。
else
{
reverse(s, i, sz - 1);
}
}
return s;
}
};
剑指Offer58-II. 左旋转字符串
https://leetcode-cn.com/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
输入: s = "abcdefg", k = 2
输出: "cdefgab"
思路
通过局部反转+整体反转达到左旋转的目的。
具体步骤为:
-
反转区间为前n(0到n - 1)的子串
-
反转区间为n到末尾(n到sz - 1)的子串
-
反转整个字符串
代码
class Solution
{
private:
void reverse(string& s, int left, int right)
{
while (left < right)
{
swap(s[left], s[right]);
++left;
--right;
}
}
public:
string reverseLeftWords(string s, int n)
{
int sz = s.size();
reverse(s, 0, n - 1);
reverse(s, n, sz - 1);
reverse(s, 0, sz - 1);
return s;
}
};
LeetCode151. 翻转字符串里的单词
https://leetcode-cn.com/problems/reverse-words-in-a-string/
给定一个字符串,逐个翻转字符串中的每个单词。
说明:
无空格字符构成一个 单词 。
输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
输入:s = " Bob Loves Alice "
输出:"Alice Loves Bob"
解释:输入字符串可以在前面或者后面或两个单词间包含多余的空格,但是反转后的字符不能包括。
思路
将整个字符串都反转过来,那么单词的顺序肯定是倒序了,只不过单词本身也倒叙了,那么再把单词反转一下,单词就正过来了。
所以解题思路如下:
- 移除多余空格
- 将整个字符串反转
- 将每个单词反转
代码
class Solution
{
private:
void removeExtraSpaces(string& s)
{
int sz = s.size();
//定义快慢指针
int slowIndex = 0, fastIndex = 0;
//删除字符串前面多余的空格
while (fastIndex < sz && s[fastIndex] == ' ')
{
++fastIndex;
}
//删除字符串中间多余的空格
for (; fastIndex < sz; ++fastIndex)
{
if (fastIndex - 1 >= 0 && s[fastIndex] == ' ' && s[fastIndex] == s[fastIndex - 1])
{
continue;
}
else
{
s[slowIndex++] = s[fastIndex];
}
}
//删除字符串后面多余的空格
if (slowIndex - 1 >= 0 && s[slowIndex - 1] == ' ')
{
s.resize(slowIndex - 1);
}
else
{
s.resize(slowIndex);
}
}
void reverse(string& s, int left, int right)
{
while (left < right)
{
swap(s[left], s[right]);
++left;
--right;
}
}
public:
string reverseWords(string s)
{
removeExtraSpaces(s);
int sz = s.size();
reverse(s, 0, sz - 1);
int left = 0, right = 0;
for (int i = 0; i < sz; ++i)
{
if (i == 0)
{
left = i;
}
if (s[i] == ' ' && s[i + 1] != ' ')
{
left = i + 1;
}
if (i == sz - 1)
{
right = sz - 1;
reverse(s, left, right);
break;
}
if (s[i] != ' ' && s[i + 1] == ' ')
{
right = i;
reverse(s, left, right);
}
}
return s;
}
};
剑指offer05. 替换空格
https://leetcode-cn.com/problems/ti-huan-kong-ge-lcof/
请实现一个函数,把字符串 s
中的每个空格替换成"%20"。
输入:s = "We are happy."
输出:"We%20are%20happy."
思路
首先扩充数组到每个空格替换成"%20"之后的大小。
使用双指针法,i指向新长度的末尾,j指向旧长度的末尾,从后向前替换空格,过程如下:
如果从前往后替换空格,那么每次添加元素都要将添加元素之后的所有元素向后移动,这样时间复杂度会达到O(N^2) 。
其实很多数组填充类的问题,都可以先预先给数组扩容到填充后的大小,然后在从后向前进行操作。
这么做有两个好处:
-
不用申请新数组。
-
从后向前填充元素,避免了从前先后填充元素时,每次添加元素都要将添加元素之后的所有元素向后移动。
代码
class Solution
{
public:
string replaceSpace(string s)
{
//统计空格数
int oldSize = s.size();
int count = 0;
for (int i = 0; i < oldSize; ++i)
{
if (s[i] == ' ')
{
++count;
}
}
int newSize = oldSize + 2 * count;
s.resize(newSize);
//i指向旧数组,j指向新数组
for (int i = oldSize - 1, j = newSize - 1; i < j; --i, --j)
{
if (s[i] != ' ')
{
s[j] = s[i];
}
else
{
s[j] = '0';
s[--j] = '2';
s[--j] = '%';
}
}
return s;
}
};
LeetCode28. 实现strStr()
https://leetcode-cn.com/problems/implement-strstr/
实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
输入: haystack = "hello", needle = "ll"
输出: 2
说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。
思路
本题是KMP算法的主要应用
1.KMP的主要思想
当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免从头再去做匹配了。
2.next数组 == 前缀表(prefix table)
- 前缀表记录下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀。
- 前缀表是用来回退的,它记录了模式串与文本串不匹配的时候,模式串应该从哪里开始重新匹配。
- 下标5(包括5)之前这部分的字符串(也就是字符串aabaa)的最长相等的前缀和后缀字符串是子字符串aa 。因为找到了最长相等的前缀和后缀,匹配失败的位置是后缀的后面,那么我们找到与其相同的前缀的后面从新匹配就可以了。
3.构造next数组
(1)初始化
(2)处理前后缀不相同的情况:回退
(3)处理前后缀相同的情况
代码
class Solution {
private:
//构造next数组
void getNext(vector<int>& next, const string& needle)
{
//初始化
int j = 0;
next[0] = 0;
//j指向前缀末尾且表示下标i之前(包括i)的字符串中相同前缀后缀的长度,i指向后缀末尾
for(int i = 1; i < next.size(); ++i)
{
//前后缀不相同的情况:回退
while (j > 0 && needle[i] != needle[j])
{
j = next[j - 1];
}
//前后缀相同的情况
if(needle[i] == needle[j])
{
++j;
}
next[i] = j;
}
}
public:
int strStr(string haystack, string needle)
{
if (needle.size() == 0) return 0;
if (haystack.size() == 0) return -1;
int size1 = haystack.size();
int size2 = needle.size();
vector<int> next(size2);
getNext(next, needle);
int j = 0;
for(int i = 0; i < size1; ++i)
{
//模式串与文本串不匹配的时候
while(j > 0 && haystack[i] != needle[j])
{
j = next[j - 1];
}
模式串与文本串匹配的时候
if(haystack[i] == needle[j])
{
++j;
}
if (j == size2)
{
return i - size2 + 1;
}
}
return -1;
}
}
LeetCode459. 重复的子字符串
https://leetcode-cn.com/problems/repeated-substring-pattern/
给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。
输入: "abab"
输出: True
解释: 可由子字符串 "ab" 重复两次构成。
思路
本题是KMP算法的另一个应用。具体思路见代码和注释。
代码
class Solution {
private:
void getNext(vector<int>& next, const string& s)
{
int j = 0;
next[0] = 0;
for (int i = 1; i < s.size(); ++i)
{
while (j > 0 && s[j] != s[i])
{
j = next[j - 1];
}
if (s[j] == s[i])
{
++j;
}
next[i] = j;
}
}
public:
bool repeatedSubstringPattern(string s)
{
int sz = s.size();
if (sz == 0) return false;
vector<int> next(sz);
getNext(next, s);
//如果(数组长度 - 最长相等前后缀长度)正好可以被数组长度整除,则说明字符串中有重复的子字符串
if (next[sz - 1] != 0 && sz % (sz - next[sz - 1]) == 0)
{
return true;
}
return false;
}
};