1. 替换空格
1.1 思路
1、使用函数库中自带的replace函数和insert函数,但会超过其时间限制
2、使用双指针,先扩充数组,预留出需要新增的位置,之后从后往前扩充,直到两指针相遇。
1.2 代码
class Solution {
public:
string replaceSpace(string s) {
//首先判断需要扩充多大位置
int count = 0;
for(int i = 0; i < s.size(); i++){
if(s[i] == ' '){
count += 2;
}
}
int left = s.size() - 1; //设置左指针
//扩充
s.resize(s.size() + count);
int right = s.size() - 1; //设置右指针
while(left < right){
if(s[left] != ' '){
s[right--] = s[left--];
}
else{
s[right--] = '0';
s[right--] = '2';
s[right--] = '%';
if(left != right){
left --;
}
}
}
return s;
}
};
2. 翻转字符串里的单词
2.1 思路
1、如果直接使用c++的库函数,会导致空间复杂度较高,同时可能会存在缓存栈溢出的情况
2、使用split函数分隔字符串,再将单词倒序相加,但是不是考察重点
3、整体思路:(1)去除多余的空格;(2)将字符串整体翻转;(3)将单词再反转。
去除多余空格:使用双指针,将多余的空格移到队尾,再resize(),做到空间复杂度为O(1),时间复杂度为O(n)。
2.2 代码
class Solution {
public:
void reverseString(string& s, int left, int right) {
while(left < right){
// char ch = s[left];
// s[left] = s[right];
// s[right] = ch;
swap(s[left++], s[right--]);
}
}
string reverseWords(string s) {
//去除多余空格
int slow = 0, fast = 0;
while(fast < s.size()){
while(s[fast] != ' ' && fast < s.size()){ //注意边界条件!!
s[slow++] = s[fast++];
}
if(slow > 0 && s[slow - 1] != ' '){
s[slow++] = ' ';
}
fast++;
}
s.resize(slow-1); //注意溢出
//翻转字符串
reverseString(s, 0, s.size()-1);
//翻转单词
for(int i = 0; i < s.size(); i++){
int k = 0;
if(s[i] != ' '){
k = i;
while(s[k] != ' ' && k <s.size()){ //注意边界条件!!!
k++;
}
// cout<<i<<k<<endl;
reverseString(s, i, k - 1);
i = k ;
}
}
return s;
}
};
3. 左旋转字符串
3.1 思路
1、若不考虑空间复杂度,咋只需要使用一个额外空间存储需要旋转的字符串,其余字符串前移,向后插入需要旋转的字符串。
2、考虑O(1)的时间复杂度,题目思路和上题类似,先做两次局部翻转,再做一次整体翻转,就可以完成左旋转字符串。
3.2 代码
class Solution {
public:
void reverseString(string& s, int left, int right){
while(left < right){
swap(s[left++], s[right--]);
}
}
string reverseLeftWords(string s, int n) {
//要实现O(1)的空间复杂度
//两次局部翻转+一次整体翻转
reverseString(s, 0, n-1);
reverseString(s, n, s.size()-1);
reverseString(s, 0, s.size()-1);
return s;
}
};
4. 实现strStr()
4.1 思路
1、使用两层循环暴力解法
2、使用KMP算法,思路放在代码里。KMP非常复杂
4.2 代码
class Solution {
public:
void getNext(int* next, string s){
//next[k]数组中的每一个元素表示的是:在字串s[0, k]的最长相等前后缀长度
int j = 0; //为了便于理解,j表示前缀子串长度
next[0] = j;
for(int i = 1; i < s.size(); i++){ //i表示后缀的比较点,j表示前缀的比较点
while(j > 0 && s[i] != s[j]){ //前缀和后缀不同,j = 0时就需要重头开始
j = next[j - 1]; //找到j之前的字串的最长相等前后缀长度,然后重新比
}
if(s[i] == s[j]){
j++; //前缀和后缀相同,继续往后比
}
next[i] = j; //记录i处的最长相等前后缀长度
}
}
int strStr(string haystack, string needle) {
if(needle.size() == 0){
return 0;
}
int next[needle.size()];
getNext(next, needle);
int j = 0;
for(int i = 0; i < haystack.size(); i++){
while(j > 0 && haystack[i] != needle[j]){
j = next[j - 1];
}
if(haystack[i] == needle[j]){
j++;
}
if(j == needle.size()){
return (i - needle.size() + 1);
}
}
return -1;
}
};
5. 重复的子字符串
5.1 思路
1、使用KMP算法,因为next[i]表示的是子串s[0, i]中的最长相等前后缀,如图所示:如果子串重复,那么s.len % ( s.len - next(s.len - 1)) == 0;
5.2 代码
class Solution {
public:
void getNext(int* next, 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) {
int len = s.size();
int next[len];
getNext(next, s);
if(len % (len - (next[len - 1])) || (next[len - 1]) == 0 ){
return false;
}
return true;
}
};