代码随想录算法训练营day09|151.翻转字符串里的单词,卡码网:55.右旋转字符串,28.实现 strStr(),459.重复的子字符串
151.翻转字符串里的单词
题目链接:https://leetcode.cn/problems/reverse-words-in-a-string/description/
暴力removeExtraSpaces:
void removeExtraSpaces(string& s) { for (int i = s.size() - 1; i > 0; i--) { if (s[i] == ' ' && s[i] == s[i - 1]) { s.erase(s.begin() + i); } } if (s.size() > 0 && s[0] == ' ') { s.erase(s.begin()); } if (s.size() > 0 && s[s.size() - 1] == ' ') { s.erase(s.begin() + s.size() - 1); } }
遇到多余空格就erase一下,时间复杂度比较高。
removeExtraSpaces版本一:
void removeExtraSpaces(string& s) { int slowIndex = 0, fastIndex = 0; while (s.size() > 0 && fastIndex < s.size() && s[fastIndex] == ' ') { fastIndex++; } for (; fastIndex < s.size(); fastIndex++) { if (fastIndex > 1 && s[fastIndex] == s[fastIndex - 1] && s[fastIndex] == ' ') { continue; } else { s[slowIndex++] = s[fastIndex]; } } if (slowIndex > 1 && s[slowIndex - 1] == ' ') { s.resize(slowIndex - 1); } else { s.resize(slowIndex); } }
一般的思考过程,就是先移除字符串前的空格,再移除中间的,再移除后面部分。
removeExtraSpaces版本二:
void removeExtraSpaces(string& s) { int slow = 0; for (int i = 0; i < s.size(); i++) { if (s[i] != ' ') { if (slow != 0) { s[slow++] = ' '; } while (i < s.size() && s[i] != ' ') { s[slow++] = s[i++]; } } } s.resize(slow); }
比版本一精简,遇到非空格就处理。
完整代码:
class Solution { public: void reverse(string& s, int start, int end) { for (int i = start, j = end; i < j; i++, j--) { swap(s[i], s[j]); } } void removeExtraSpaces(string& s) { int slow = 0; for (int i = 0; i < s.size(); i++) { if (s[i] != ' ') { if (slow != 0) { s[slow++] = ' '; } while (i < s.size() && s[i] != ' ') { s[slow++] = s[i++]; } } } s.resize(slow); } string reverseWords(string s) { removeExtraSpaces(s); reverse(s, 0, s.size() - 1); int start = 0; for (int i = 0; i <= s.size(); ++i) { if (s[i] == ' ' || i == s.size()) { reverse(s, start, i - 1); start = i + 1; } } return s; } };
比较综合的一道题目。
卡码网:55.右旋转字符串
题目链接:https://kamacoder.com/problempage.php?pid=1065
我的代码:
#include <iostream> using namespace std; void reverse(string& s, int start, int end) { for (int i = start, j = end; i < j; i++, j--) { swap(s[i], s[j]); } } int main() { string s; int n; cin >> n >> s; reverse(s, 0, s.size() - 1); reverse(s, 0, n - 1); reverse(s, n, s.size() - 1); cout << s << endl; return 0; }
通过三次倒序,一次整体倒序,两次分段倒序,得到右旋子串。
28.实现 strStr()
题目链接:https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string/description/
前缀表(不减一)C++实现:
class Solution { public: void getNext(int* next, const string& s) { int j = 0; next[0] = j; 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; } } int strStr(string haystack, string needle) { vector<int> next(needle.size()); getNext(&next[0], needle); int j = 0; for (int i = 0; i < haystack.size(); i++) { while (j > 0 && needle[j] != haystack[i]) { j = next[j - 1]; } if (needle[j] == haystack[i]) { j++; } if (j == needle.size()) { return i - j + 1; } } return -1; } };
直接用前缀表作为next数组的值,KMP算法的典型例题。
前缀表统一减一C++实现:
class Solution { public: void getNext(int* next, const string& s) { int j = -1; next[0] = j; for (int i = 1; i < s.size(); i++) { while (j >= 0 && s[j + 1] != s[i]) { j = next[j]; } if (s[j + 1] == s[i]) { j++; } next[i] = j; } } int strStr(string haystack, string needle) { vector<int> next(needle.size()); getNext(&next[0], needle); int j = -1; for (int i = 0; i < haystack.size(); i++) { while (j >= 0 && needle[j + 1] != haystack[i]) { j = next[j]; } if (needle[j + 1] == haystack[i]) { j++; } if (j == needle.size() - 1) { return i - j; } } return -1; } };
前缀表减一作为next数组的值,KMP算法的典型例题。
459.重复的子字符串
题目链接:https://leetcode.cn/problems/repeated-substring-pattern/description/
移动匹配法:
class Solution { public: bool repeatedSubstringPattern(string s) { string t = s + s; t.erase(t.begin()); t.erase(t.end() - 1); if (t.find(s) != std::string::npos) return true; return false; } };
判断字符串s是否由重复子串组成,只要两个s拼接在一起,里面还出现一个s的话,就说明是由重复子串组成,要注意刨除 s + s 的首字符和尾字符,这样避免在s+s中搜索出原来的s。
KMP(前缀表统一减一):
class Solution { public: void getNext(int* next, const string& s) { int j = -1; next[0] = j; for (int i = 1; i < s.size(); i++) { while (j >= 0 && s[j + 1] != s[i]) { j = next[j]; } if (s[j + 1] == s[i]) { j++; } next[i] = j; } } bool repeatedSubstringPattern(string s) { vector<int> next(s.size()); getNext(&next[0], s); int len = s.size(); if (next[len - 1] != -1 && len % (len - (next[len - 1] + 1)) == 0) { return true; } return false; } };
如果 next[len - 1] != -1,则说明字符串有最长相同的前后缀,如果len % (len - (next[len - 1] + 1)) == 0 ,则说明数组的长度正好可以被 (数组长度-最长相等前后缀的长度) 整除 ,说明该字符串有重复的子字符串。
KMP(前缀表不减一):
class Solution { public: void getNext(int* next, const string& s) { int j = 0; next[0] = j; 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; } } bool repeatedSubstringPattern(string s) { vector<int> next(s.size()); getNext(&next[0], s); int len = s.size(); if (next[len - 1] != 0 && len % (len - next[len - 1]) == 0) { return true; } return false; } };
同上。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步