今日题目
题目 | 难度 | 备注 |
---|---|---|
344. 反转字符串 | 简单 | |
541. 反转字符串 II | 简单 | 考察模拟过程能力 |
151. 反转字符串中的单词 | 中等 | |
28. 找出字符串中第一个匹配项的下标 | 简单 | KMP |
459. 重复的子字符串 | 简单 | ♥ |
字符串篇
题目:344. 反转字符串
一、源代码
//头尾交换即是反转
void reverseString(vector<char>& s) {
for (int i = 0; i < s.size()/2; i++){
int temp = s[i];
s[i] = s[s.size()-i-1];
s[s.size()-i-1] = temp;
}
}
二、代码思路
经典数组 / 字符串反转代码,首尾元素交换即为反转
题目:[541. 反转字符串 II
一、源代码
class Solution {
private:
string reverseBtw(string s, int l, int r) {
for (int i = l; i <= (l + r) / 2; i++) {
int temp = s[i];
s[i] = s[r + l - i];
s[r + l - i] = temp;
}
return s;
}
public:
string reverseStr(string s, int k) {
int len = s.size();
for (int i = 0; i <= len; i += 2 * k) {
if (i != 0) {
s = reverseBtw(s, i - 2 * k, i - k - 1);
}
if (len - i < k) { // 如果剩余字符少于k个
s = reverseBtw(s, i, len - 1);
break;
} else if (len - i < 2 * k) { // 如果剩余字符少于2k个
s = reverseBtw(s, i, i + k - 1);
break;
}
}
return s;
}
};
二、代码思路
从1开始计数,在计数开始前或每数到 2k 时都进行判断处理一次 ①数到2k的倍数则反转前k个,②剩余字符少于k个则全部反转并结束,③ 如果剩余字符少于2k个 但大于等于 k 个则反转前k个并结束。
要实现:从1开始计数,在计数开始前或每数到 k 时都进行某种操作,可定义 for ( i = 0; i <= len; i+=k) 以及用 i % k == 0 来判断
三、优化思路
模拟过程,显然观察区长度为 2k,遍历每个观察区进行处理。对每个观察区,有两种处理情况:
① 反转前 k 个,条件为剩余字符数大于等于 k个
② 剩余字符全部反转,条件为剩余字符数小于 k个
显然观察区长度为 2k,可用 for 循环遍历观察区 for (int i = 0; i < n; i += 2 * k) ,i 为观察区第一个元素下标,则 ① 为 reverse(s.begin() + i, s.begin() + i + k); ② 为 reverse(s.begin() + i, s.begin() + n); 若 i + k > n,说明此观察区剩余字符数量小于 k 个,应全部反转
四、优化代码
class Solution {
public:
string reverseStr(string s, int k) {
int n = s.length();
for (int i = 0; i < n; i += 2 * k) {
reverse(s.begin() + i, s.begin() + min(i + k, n));
}
return s;
}
};
题目:151. 反转字符串中的单词
一、源代码
string reverseWords(string s) {
vector<string> wd;
int i = 0;
string ans = "";
while (i < s.size()) {
string temp = "";
while (s[i] != ' ' && i < s.size()) { // 获取单词
temp += s[i++];
}
if (temp != "") { //单词不为空则存起来
wd.push_back(temp);
}
if (i < s.size()) {
i++;
}
}
for (int j = wd.size() - 1; j >= 0; j--) {
ans += wd[j];
if (j > 0) {
ans += ' ';
}
}
return ans;
}
二、代码思路
把字符串拆分成一个个的单词,压入数组,最后逆序遍历数组,即为结果。
题目:28. 找出字符串中第一个匹配项的下标
28. 找出字符串中第一个匹配项的下标 - 力扣(LeetCode)
一、源代码
int strStr(string haystack, string needle) {
int lenH = haystack.size(), lenD = needle.size();
if (lenH < lenD) {
return -1;
}
for (int i = 0; i <= lenH - lenD; i++) { //遍历haystack
int cnt = 0,p = i, j = 0;
while (haystack[p] == needle[j] && p < lenH && j < lenD){ //若元素相同则进行匹配cnt++;
++cnt; ++p; ++j;
}
if (cnt == lenD){ //若匹配成功
return i;
}
}
return -1;
}
二、优化思路
源代码为简单暴力匹配,字符串模式匹配要时间复杂度低当然得用KMP算法
三、优化代码
int strStr(string s, string p) {
int n = s.size(), m = p.size();
if(m == 0) return 0;
//设置哨兵
s.insert(s.begin(),' ');
p.insert(p.begin(),' ');
vector<int> next(m + 1);
//预处理next数组
for(int i = 2, j = 0; i <= m; i++){
while(j and p[i] != p[j + 1]) j = next[j];
if(p[i] == p[j + 1]) j++;
next[i] = j;
}
//匹配过程
for(int i = 1, j = 0; i <= n; i++){
while(j and s[i] != p[j + 1]) j = next[j];
if(s[i] == p[j + 1]) j++;
if(j == m) return i - m;
}
return -1;
}
题目:459. 重复的子字符串
一、源代码
class Solution {
private:
bool canCompa(string s, string sb) { //判断s是不是由sb组成的
while (s.find(sb) != s.npos) {
s.erase(s.find(sb), sb.size());
}
return s.size() == 0 ? true : false;
}
public:
bool repeatedSubstringPattern(string s) {
if (s.size() == 1)
return false;
int p = 2; // 划分字符串
string sb = s;
while (sb.size() != 1) {
if (s.size() % p == 0 || s.size() / p == 1) { //可将s均分,或拆成长度为1的子串
sb = s.substr(0, s.size() / p); //将s拆分成长度不同的子串
if(canCompa(s, sb)){
return true;
}
}
++p;
}
return false;
}
};
二、优化思路
若 s 可由若干相同字串 s‘ 组成,即 s = s’ s’ … s’ 。有性质:将 s 中第一个(或前k个) s‘ 移去,再 s 末尾加入一个 (或k个)s’ ,则 s 不变。所以若 s = s’ s’ … s’ ,将 s 的首尾拼接(ss)后,中间会出现 s。ss = s’ s’ … s’ s’ s’ … s’ ,中间一定有s的出现。所以若将两个s 连在一起,并移除第一个和最后一个字符,那么得到的字符一定包含 s。
可用这个性质进行判断,不用枚举子串判断。
三、优化代码
class Solution {
public:
bool repeatedSubstringPattern(string s) {
return (s + s).find(s, 1) != s.size(); // ss拼接,从下标为1的位置开始找s
}
};