程式碼隨想錄演算法訓練營day09|151.翻轉字串裡的單詞,卡碼網:55.右旋轉字串,28.實現 strStr(),459.重複的子字串

kurumaruq發表於2024-08-17

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;
    }
};

同上。

相關文章