目录
- 一、反转字符串
 - 二、反转字符串 II
 - 三、替换空格
 - 四、反转字符串中的单词
 - 五、左旋转字符串
 - 六、找出字符串中第一个匹配项的下标(KMP算法实现)
 - 七、重复的子字符串
 
一、反转字符串
Leetcode 344
class Solution {
public:
    void reverseString(vector<char>& s) {
        for (int i = 0; i < s.size() / 2; i ++ )
            swap(s[i], s[s.size() - 1 - i]);
    }
};
 
二、反转字符串 II
Leetcode 541
使用库函数 reverse()
class Solution {
public:
    string reverseStr(string s, int k) {
        for (int i = 0; i < s.size(); i += (2 * k)) {
            if (i + k <= s.size())
                reverse(s.begin() + i, s.begin() + i + k);
            else reverse(s.begin() + i, s.end());
        }
        return s;
    }
};
 
三、替换空格
剑指 Offer 05
为了将时间复杂度做到 O ( n ) O(n) O(n),空间复杂度做到 O ( 1 ) O(1) O(1),可以先统计字符串中空格的个数,然后将字符串长度扩充到替换后的字符串的长度。接下来,采用双指针从后往前遍历, i i i 指向新长度的末尾, j j j 指向旧长度的末尾,遇到空格就替换掉。
class Solution {
public:
    string replaceSpace(string s) {
        int cnt = 0, len_old = s.size();
        for (char c: s) // 统计空格个数
            if (c == ' ') cnt ++ ;
        s.resize(len_old + cnt * 2);
        int len_new = s.size();
        for (int i = len_new - 1, j = len_old - 1; j < i; i -- , j -- ) 
            if (s[j] != ' ') s[i] = s[j];
            else {
                s[i] = '0', s[i - 1] = '2', s[i - 2] = '%';
                i -= 2;
            }
        return s;
    }
};
 
四、反转字符串中的单词
Leetcode 151
- 先去掉多余空格(快慢指针)
 - 翻转整个字符串
 - 翻转每个单词
 
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 removeExtraSpace(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) {
        removeExtraSpace(s);
        reverse(s, 0, s.size() - 1);
        int start = 0;
        for (int i = 0; i <= s.size(); i ++ )
            if (i == s.size() || s[i] == ' ') {
                reverse(s, start, i - 1);
                start = i + 1;
            }
        return s;
    }
};
 
五、左旋转字符串
剑指 Offer 58 -Ⅱ
class Solution {
public:
    string reverseLeftWords(string s, int n) {
        reverse(s.begin(), s.begin() + n);
        reverse(s.begin() + n, s.end());
        reverse(s.begin(), s.end());
        return s;
    }
};
 
六、找出字符串中第一个匹配项的下标(KMP算法实现)
Leetcode 28
详细 KMP 算法解释见参考文章
看文章时应该注意几点:
- 前后缀数组的含义,注意理解图中这句话

 - next 数组与前后缀数组的联系
 
class Solution {
public:
    void getNext(int *next, const string& s) {
        next[0] = 0;
        for (int i = 1, j = 0; i < s.size(); i ++ ) {
            while (j && s[i] != s[j]) j = next[j - 1];
            if (s[i] == s[j]) j ++ ;
            next[i] = j;
        }
    }
    int strStr(string s, string t) {
        int next[t.size()];
        getNext(next, t);
        for (int i = 0, j = 0; i < s.size(); i ++ ) {
            while (j && s[i] != t[j]) j = next[j - 1];
            if (s[i] == t[j]) j ++ ;
            if (j == t.size()) return i - t.size() + 1;
        }
        return -1;
    }
};
 
七、重复的子字符串
Leetcode 459
解题关键:在由重复子串组成的字符串中,最长相等前后缀不包含的子串就是最小重复子串
参考题解
class Solution {
public:
    void getNext(int* next, const string& s) {
        next[0] = 0;
        for (int i = 1, j = 0; i < s.size(); i ++ ) {
            while (j && s[i] != s[j]) j = next[j - 1];
            if (s[i] == s[j]) j ++ ;
            next[i] = j;
        }
    }
    bool repeatedSubstringPattern(string s) {
        int next[s.size()];
        getNext(next, s);
        int len = s.size();
        if (next[len - 1] != 0 && len % (len - next[len - 1]) == 0) 
            return true;
        return false;
    }
};
                

















