【算法训练 day8 字符串】


一、反转字符串-LeetCode 344

Leecode链接: leetcode 344
文章链接: 代码随想录
视频链接: B站

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

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

示例:

输入:s = ["h","e","l","l","o"]
输出:["o","l","l","e","h"]

思路

双指针,一个指向头,一个指向尾,逐个交换数组元素,直到左指针在右指针右边时退出循环。

实现代码

//cpp
class Solution {
public:
    void reverseString(vector<char>& s) {
        int left,right;
        left = 0;
        right = s.size()-1;

        while(left<right){
            char t = s[right];
            s[right] = s[left];
            s[left] = t;
            right--;
            left++;
        }
    }
};

个人问题

可以用库函数swap,无所谓,基本原理都一样。

总结

整体简单,算法时间复杂度为O(n),空间复杂度为O(1)。


二、反转字符串Ⅱ-LeetCode 541

Leecode链接: LeetCode 541
文章链接: 代码随想录
视频链接: B站

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

如果剩余字符少于 k 个,则将剩余字符全部反转。

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

示例:

输入:s = "abcdefg", k = 2
输出:"bacdfeg"

思路

我的思路是:首先计算字符总数,然后对2k进行除计算,这样我就知道需要进行多少次字符反转,然后计算求余结果,判断剩余字符的个数然后进行剩余字符的反转操作。卡哥思路是使用for循环每次加一个2k,直到指针不满足要求时,执行特殊操作

实现代码

个人代码

//cpp
class Solution {
public:
    void myreverse(string& s,int left,int right){
        while(left<right){
            swap(s[left++],s[right--]);
        }
    }

    string reverseStr(string s, int k) {
        int count = 0;
        int left = 0;
        int right = k-1;
        int len = s.size();
        
        while(count<len/(2*k)){
            myreverse(s,left+(count*2*k),right+((count)*2*k));
            count++;
        }
		
		//大于k
        if(len%(2*k)>=k){
            myreverse(s,count*2*k,count*2*k+k-1);
        }
        //小于k
        else if(len%(2*k)<k&&len%(2*k)>0){
            myreverse(s,count*2*k,len-1);
        }
        //无剩余元素直接返回字符串
        else if(len%(2*k) == 0){
            return s;
        }
        return s;
    }
};

精简代码

//cpp
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]);
        }
    }
    string reverseStr(string s, int k) {
        for (int i = 0; i < s.size(); i += (2 * k)) {
            // 1. 每隔 2k 个字符的前 k 个字符进行反转
            // 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
            if (i + k <= s.size()) {
                reverse(s, i, i + k - 1);
                continue;
            }
            // 3. 剩余字符少于 k 个,则将剩余字符全部反转。
            reverse(s, i, s.size() - 1);
        }
        return s;
    }
};

个人问题

代码不够精炼。

总结

简单题目,时间复杂度O(n),空间复杂度为O(1)。


三.替换数字

链接: 卡码网
文章链接: 代码随想录

给定一个字符串 s,它包含小写字母和数字字符,请编写一个函数,将字符串中的字母字符保持不变,而将每个数字字符替换为number。

示例:

输入:"a1b2c3"
输出:"anumberbnumbercnumber"

思路

不少人直接输出答案,有点违背题目意图。正常的真实返回一个字符串的情况下有两种办法:第一种很自然的想到用额外空间再创建一个数组存放新的字符串,字符直接赋值,数字赋值为“number”;第二种使用双指针,计算好原字符串的数字个数,然后对数组进行扩充空间,扩充空间的大小为能存放转化后的字符串大小。让右指针指向结尾,左指针指向原字符串结尾,如果是字符,直接赋值并同时移动两个指针;如果是数字,逐个赋值number字符。

实现代码

个人代码

//cpp
string replace(string s){
    int len = s.size();
    string res;

    for(int i = 0;i<len;i++){
        if(s[i]>='0'&&s[i]<='9'){
            res = res + "number";
        }
        else{
            res = res + s[i];
        }
    }

    return res;
}

双指针

//cpp
int main() {
    string s;
    while (cin >> s) {
        int sOldIndex = s.size() - 1;
        int count = 0; // 统计数字的个数
        for (int i = 0; i < s.size(); i++) {
            if (s[i] >= '0' && s[i] <= '9') {
                count++;
            }
        }
        // 扩充字符串s的大小,也就是将每个数字替换成"number"之后的大小
        s.resize(s.size() + count * 5);
        int sNewIndex = s.size() - 1;
        // 从后往前将数字替换为"number"
        while (sOldIndex >= 0) {
            if (s[sOldIndex] >= '0' && s[sOldIndex] <= '9') {
                s[sNewIndex--] = 'r';
                s[sNewIndex--] = 'e';
                s[sNewIndex--] = 'b';
                s[sNewIndex--] = 'm';
                s[sNewIndex--] = 'u';
                s[sNewIndex--] = 'n';
            } else {
                s[sNewIndex--] = s[sOldIndex];
            }
            sOldIndex--;
        }
        cout << s << endl;       
    }
}

个人问题

没有想到两种办法。

总结

简单题目。时间复杂度O(n),空间复杂度O(n)或者O(1)。


四.翻转字符串里的单词-LeeCode 151

Leecode链接: LeetCode 151
文章链接: 代码随想录
视频链接: B站

给定一个字符串,逐个翻转字符串中的每个单词。

示例:

输入: "the sky is blue"
输出: "blue is sky the"

输入: "  hello world!  "
输出: "world! hello"
解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。

输入: "a good   example"
输出: "example good a"
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。

思路

具体思路:首先去除多余空格,然后反转整个字符串,最后针对单个单词进行翻转。

实现代码

//cpp
void removeSpace(string& s){
    int slow = 0;
    int fast = 0;
    int len = s.size();

    for(fast;fast<len;fast++){
        if(s[fast] != ' '){
            if(slow != 0){//条件成立表示这时慢指针不指向第一个字母,可以考虑是单词结束了需要加一个空格
                s[slow++] = ' ';
            }
            while(fast<len&&s[fast] != ' '){//单个单词内循环覆盖,遇到空格退出,如果不加while会导致被上面的空格覆盖
                s[slow++] = s[fast++];
            }
            
        }
    }
    s.resize(slow);//如果没有这一步,可能导致有重复字符
}

//整个字符串翻转
void reverseWord(string& s,int left,int right){

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

string reverseWord(string& s){
    removeSpace(s);//去除空格
    reverseWord(s,0,s.size()-1);//字符串整体翻转

    for(int i = 0,j = 0;i<=s.size();i++){//按照单词分段翻转
        if(s[i] == ' '||i == s.size()){
            reverseWord(s,j,i-1);
            j = i + 1;
        }
    }

    return s;
}

个人问题

没有写出代码,卡在了如何将多余空格删除那一步,核心思想与删除指定元素那题相似,但有差别,原有只需要一层for循环,现在需要加一层循环用于对单个单词赋值。一般的,如果发现空格,需要等待快指针找到不是空格字符时将字符赋值给慢指针,赋值完后每个单词结尾需要添加一个空格,这就需要判断何时一个单词结束,判断依据是:每次快指针赋值给慢指针遇到空格时退出了代表着单词结束,这时候就需要添加空格,加空格需要在单词赋值前进行,也就是在进行下一次单词赋值前进行添加上一个单词后的空格,如果在while之后进行,会导致字符串末尾多一个空格。还需注意删除空格后需要重新调整字符串大小,否则会有重复字符。

总结

题目有一定难度,需要理清逻辑。时间复杂度为O(n),空间复杂度为O(1)。


五.右旋字符串

链接: 卡码网
文章链接: 代码随想录

字符串的右旋转操作是把字符串尾部的若干个字符转移到字符串的前面。给定一个字符串 s 和一个正整数 k,请编写一个函数,将字符串中的后面 k 个字符移到字符串的前面,实现字符串的右旋转操作。

示例:

输入:
2
abcdefg

输出:
fgabcde

思路

字符串整体翻转一下,然后根据k的大小,逐段翻转。

实现代码

//cpp
//翻转函数,可以用库函数reverse
void reversestr(string& s,int left,int right){
    while(left<right){
        swap(s[left++],s[right--]);
    }
}

string strright(string& s,int k){
    reversestr(s,0,s.size()-1);//整体翻转
    
    reversestr(s,0,k-1);//分段翻转
    reversestr(s,k,s.size()-1);//分段翻转
    return s;
}

个人问题

做过上一题,这道题应该秒解,无明显问题。

总结

时间复杂度为O(n),空间复杂度为O(1)。

  • 18
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值