代码随想录算法训练营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;
    }
};

同上。

posted @ 2024-08-17 14:05  kurumaruq  阅读(10)  评论(0)    收藏  举报