leetcode解题字符串篇

字符串

1. 344.反转字符串

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

class Solution {
public:
    void reverseString(vector<char>& s) {
        
        int left = 0;
        int right = s.size() - 1;
        while(left < right){
            char tmp = s[left];
            s[left] = s[right];
            s[right] = tmp;
            left++;
            right--;
        }
    }
};

原地修改,直接双指针分别指向字符串两端,然后交换即可。

2. 541. 反转字符串II

给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。

如果剩余字符少于 k 个,则将剩余字符全部反转。 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。

class Solution {
public:
    string reverseStr(string s, int k) {

        int left,right;
        for(int i = 0 ; i < s.size() ; i++){
            if((i+1)%(2*k) == 0){
                left = i + 1 - 2*k;
                right = i - k;
                while(left < right){
                    swap(s[left],s[right]);
                    left++;
                    right--;
                }
            }
        }

        if(s.size()%(2*k) < k){
            left = s.size() - s.size()%(2*k);
            right = s.size() - 1;
        }else{
            left = s.size() - s.size()%(2*k);
            right = s.size() - 1 - (s.size()%(2*k) - k);
        }

        while(left < right){
            swap(s[left],s[right]);
            left++;
            right--;
        }

        return s;
    }

};

这题相当于是局部反转,判断好局部反转的起始位置和终止位置即可正确完成题目,主要是起始位置和终止位置的确定,得看好,索引从0开始,而个数从1开始,所以类似题最好能够带具体的值去检查一遍起始值和终止值。

3. 剑指Offer 05.替换空格

请实现一个函数,把字符串 s中的每个空格替换成"%20"。

class Solution {
public:
    string replaceSpace(string s) {

        string result;
        for(auto i : s){
            if(i==' '){
                result.push_back('%');
                result.push_back('2');
                result.push_back('0');
            }else{
                result.push_back(i);
            }
        }
        return result;
    }
};

// 方法二
class Solution {
public:
    string replaceSpace(string s) {

        int count = 0;
        int initLength = s.size();
        for(auto i : s){
            if(i == ' ')
                count++;
        }
        s.resize(initLength + count*2);
        int left = initLength - 1;
        int right = s.size() - 1;
        while(left >= 0){
            if(s[left] == ' '){
                s[right--] = '0';
                s[right--] = '2';
                s[right--] = '%';
                left--;
            }else{
                s[right--] = s[left--];
            }
        }
        return s;

    }
};

将c++中的string当成数组看就行了,不难。方法一是申请了额外的空间来保存新的字符串,方法二是直接修改的原字符串。先遍历字符串,求出空格的数量,然后对字符串进行扩容,然后从右往左进行遍历赋值,左指针初始位置为原字符串末尾,右指针为新字符串末尾。

其实很多数组填充类的问题,都可以先预先给数组扩容带填充后的大小,然后在从后向前进行操作,这么做有两个好处:

  1. 不用申请新数组。
  2. 从后向前填充元素,避免了从前先后填充元素要来的 每次添加元素都要将添加元素之后的所有元素向后移动。

4. 151.翻转字符串里的单词

给你一个字符串 s ,逐个翻转字符串中的所有 单词 。单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。请你返回一个翻转 s 中单词顺序并用单个空格相连的字符串。

说明:

  1. 输入字符串 s 可以在前面、后面或者单词间包含多余的空格。
  2. 翻转后单词间应当仅用一个空格分隔。
  3. 翻转后的字符串中不应包含额外的空格。
class Solution {
public:
    string reverseWords(string s) {

        vector<string> vec;
        string tmp;
        for(auto i : s){
            if(i == ' ' && tmp.empty()){
                continue;
            }else if(i == ' ' && !tmp.empty()){
                vec.push_back(tmp);
                tmp.clear();
            }else{
                tmp.push_back(i);
            }
        }
        if(!tmp.empty())
            vec.push_back(tmp);

        string result;
        for(int i = vec.size() - 1 ; i >=0 ; i--){
            result += vec[i];
            result += " ";
        }
        result.pop_back();
        return result;
    }
};

方法一解题其实还是有些许放水,因为本质上反转可以直接调用split,解法1我也是使用了额外的数组空间来做,总的来说就是将单词存在vector中,然后再还原到string中,整体思路比较简单。

5. 剑指Offer58-II.左旋转字符串

class Solution {
public:
    string reverseLeftWords(string s, int n) {

        // 两次反转字符串

        int left = 0 , right = s.size() - 1;

        while(left<right){
            swap(s[left],s[right]);
            left++;
            right--;
        }
        left = 0, right = s.size() - n - 1;
        while(left<right){
            swap(s[left],s[right]);
            left++;
            right--;
        }

        left = s.size() - n, right = s.size() - 1;
        while(left<right){
            swap(s[left],s[right]);
            left++;
            right--;
        }

        return s;

    }
};

通过多次局部旋转,达到目的。

6. 28. 实现 strStr()

实现 strStr() 函数。

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/implement-strstr
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

class Solution {
public:
    int strStr(string haystack, string needle) {
        int n = haystack.size(), m = needle.size();
        for (int i = 0; i + m <= n; i++) {
            bool flag = true;
            for (int j = 0; j < m; j++) {
                if (haystack[i + j] != needle[j]) {
                    flag = false;
                    break;
                }
            }
            if (flag) {
                return i;
            }
        }
        return -1;
    }
};

class Solution {
public:
    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 && needle[j] != haystack[i]){
                j = next[j - 1];
            }
            if(needle[j] == haystack[i])
                j++;
            if(j == needle.size())
                return i - j + 1;
        }
        return -1;

    }

    void getNext(int * next , string needle){
        int j = 0;
        next[0] = j;

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

方法1为暴力解法,方法二为KMP解法。具体做法可以看我转载的博客。

7. 459.重复的子字符串

给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。

class Solution {
public:
    bool repeatedSubstringPattern(string s) {

        if(s.size()==1||s.size()==0)
                return false;

        for(int i = 1 ; i <= s.size() / 2 ; i++){
            if(s.size() % i == 0){
                int count = s.size() / i - 1;
                string tmp = s.substr(0,i);
                string t = tmp;
                while(count!=0){
                    
                    tmp = tmp + t;
                    count--;
                }
                
                if(tmp==s){
                    return true;
                }
            }
        }

        return false;

    }
};

class Solution {
public:
    void getNext (int* next, const string& s){
        next[0] = 0;
        int j = 0;
        for(int i = 1;i < s.size(); i++){
            while(j > 0 && s[i] != s[j]) {
                j = next[j - 1];
            }
            if(s[i] == s[j]) {
                j++;
            }
            next[i] = j;
        }
    }
    bool repeatedSubstringPattern (string s) {
        if (s.size() == 0) {
            return false;
        }
        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;
    }
};

第一种暴力解法,直接暴毙超时,第二种是利用了KMP算法,先求出next前缀表数组,如果s字符串是满足题意的话,那么必然从第二个重复的字串开始next对应的元素就是不断累加的,1 2 3 4 5 6 7 8 ,所以用len % (len - (next[len - 1] ))判断,如果len % (len - (next[len - 1])) == 0 ,则说明 (数组长度-最长相等前后缀的长度) 正好可以被 数组的长度整除,说明有该字符串有重复的子字符串。数组长度减去最长相同前后缀的长度相当于是第一个周期的长度,也就是一个周期的长度,如果这个周期可以被整除,就说明整个数组就是这个周期的循环。(注:下图为next实现为-1时候的图片)【也就相当于是周期长度可以被next[len - 1]整除,保证了在字符串结束的时候进行了一个完整的周期。因为len = next[len - 1] + 周期长度,所以代码中直接使用len来除,其实用next[len - 1]来检查是否整除也是OK的】

如果截止到下图的2的位置(第二个f的位置),那么一个周期长度就是5,就不能被整除,就不符合题意。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值