代码随想录算法训练营day 9 | Leetcode 151 28 459 Kama 55

Leetcode 反转字符串的单词

首先去除多余空格,首尾没有空格并且单词间只有一个空格。其次,翻转整个字符串。最后,翻转每个单词。

class Solution {
public:
    void reverse(string& s, int begin, int end){
        while(begin < end){
            swap(s[begin], s[end]);
            begin++;
            end--;
        }
    }
    string reverseWords(string s) {
        //删除多余空格
        int fast = 0;
        int slow = 0;
        while(fast < s.size()){
            if(s[fast] != ' '){
                s[slow] = s[fast];
                slow++;
                if(s[fast + 1] == ' ' || fast == s.size() - 1){
                    s[slow] = ' ';
                    slow++;
                }
            }
            fast++;
        }
        s.resize(slow - 1);
        //翻转字符串
        reverse(s, 0, slow - 2);
        //翻转单词
        fast = 0, slow = 0;
        while(fast <= s.size()){
            if(s[fast] == ' ' || fast == s.size()){
                reverse(s, slow, fast - 1);
                slow = fast + 1;
            }
            fast++;
        }

        return s;
    }
};

Kama 55 右旋字符串

刚开始一直在想元素移动覆盖的问题,最后还是保留了最末的值,用一个for用前面的值不断覆盖后面的值。

#include <iostream>

using namespace std;

void rightMove(string& s){
    char tmp = s[s.size() - 1];
    for(int i = s.size() - 1; i > 0; i--){
        s[i] = s[i-1];
    }
    s[0] = tmp;
}

int main(){
    int n;
    string s;
    cin >> n >> s;
    while(n-- >0){
        rightMove(s);
    }
    cout<<s<<endl;
    
}

然后看到官解,可以把字符串分为两部分L-n和n,先整体翻转字符串然后局部翻转两个子字符串。

#include <iostream>

using namespace std;

void reverse(string& s, int begin, int end){
    while(begin < end){
        swap(s[begin], s[end]);
        begin++;
        end--;
    }
}

int main(){
    int n;
    string s;
    cin >> n >> s;
    reverse(s, 0, s.size() - 1);
    reverse(s, 0, n - 1);
    reverse(s, n, s.size() - 1);
    cout<<s<<endl;
    
}

Leetcode 28 找出字符串中第一个匹配项的下标

首先,想到暴力解法,两层for循环。

class Solution {
public:
    int strStr(string haystack, string needle) {
        for(int i = 0; i < haystack.size(); i++){
            int j = 0;
            int k = i;
            while(j < needle.size() &&haystack[k] == needle[j]){
                k++;
                j++;
            }
            if(j >= needle.size() ){
                return i;
            }
        }
        return -1;
    }
};

然后学习了KMP算法。在暴力求解的时候,当我们选择从haystack的第0个元素开始遍历,发现到第i个元素与needle不相同时,会直接从第1个元素开始遍历。这里没有利用到前一次遍历的信息。而KMP算法就是希望能够利用上之前遍历的信息(匹配的内容),避免每次从头再做匹配。这里使用了一个前缀表来实现。前缀表next的第j个元素,记录了needle从第0 - j个元素构成的子字符串的最大相同前后缀的长度。当haystack从第i个元素开始,匹配到第i + j个元素失败时,此时needle对应匹配到第j个元素,暴力解法选择从第i+1个元素重新开始,而根据前缀表next可知,needle中的第0到next[j] -1元素,与第j - next[j] + 1到第j元素是相同的,因此haystack下一次遍历可以从对应的第i+j - next[j] +1个元素开始。

而这个题只要求返回第一次匹配的下标,因此实际可以继续从haystack的第i+j元素匹配,只是要注意对应的needle要从第next[j]开始匹配。

如果对next表统一减一则可以得到下面的代码:

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[i] != s[j + 1]){
                j = next[j];
            }
            if(s[i] == s[j + 1]){
                j++;
            }
            next[i] = j;
        }
    }

    int strStr(string haystack, string needle) {
        if(needle.size() == 0){
        return 0;
        }
        vector<int> next(needle.size());

        getNext(&next[0], needle);
        
        int j = -1;
        for(int i = 0; i < haystack.size(); i++){
            while( j >= 0 && haystack[i] != needle[j + 1]){
                j = next[j];
            }
            if(haystack[i] == needle[j + 1]){
                j++;
            }
            if(j == (needle.size() - 1)){
                return (i - needle.size() + 1);
            }
        }

        return -1;
    }
};

Leetcode 459 重复的子字符串

这道题还是很巧妙的。首先想到检查一个字符串里是否有另一个字符串要先想到KMP方法,而KMP方法的核心就是利用了一个前缀表记录了到每个元素的最大相同前后缀长度,对于字符串s,如果他能够仅被一个字符串重复构造,那么这个最小构造字符串一定是s的最大的前后缀所不相交的地方,换言之,最小构造字符串的长度一定等于s.size()-next[j].

class Solution {
public:
    bool repeatedSubstringPattern(string s) {
        int j = -1;
        vector<int> next(s.size());
        next[0] = j;
        for(int i = 1; i < s.size(); i++){
            while(j >= 0 && s[i] != s[j + 1]){
                j = next[j];
            }
            if(s[i] == s[j + 1]){
                j++;
            }
            next[i] = j;
        }
        int len =s.size();
        if( next[len - 1] != -1 && len % (len - (next[len - 1] + 1)) == 0){
            return true;
        }else{
            return false;
        }
    }
};

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值