day8 | 344.反转字符串、541. 反转字符串II、 剑指Offer 05.替换空格、151.翻转字符串里的单词、剑指Offer58-II.左旋转字符串

目录:

解题及思路学习

344.反转字符串

https://leetcode.cn/problems/reverse-string/

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

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

示例 1:

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

思考:用双指针完成。

class Solution {
public:
    void reverseString(vector<char>& s) {
        char temp;
        for (int i = 0, j = s.size() - 1; i <= j; i++, j--) {
            temp = s[i];
            s[i] = s[j];
            s[j] = temp;
        }
        
}
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

也可以使用swap函数进行交换

class Solution {
public:
    void reverseString(vector<char>& s) {
        for (int i = 0, j = s.size() - 1; i <= j; i++, j--) {
            swap(s[i], s[j]);
        }   
}
};

但是用库函数的速度会慢一些。

541. 反转字符串II

https://leetcode.cn/problems/reverse-string-ii/

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

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

示例 1:

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

思考:写一个反转字符串的函数。将总的长度除以k,则对于最后尾部的也全部反转。前面的就隔k个反转k个。

需要多注意边界问题。

class Solution {
public:
    void reverseString(string& s, int start, int end) {
        char temp;
        for (int i = start, j = end; i <= j; i++, j--) {
            temp = s[i];
            s[i] = s[j];
            s[j] = temp;
        }
    }
    string reverseStr(string s, int k) {
        for (int i = 0; i < s.size(); i += 2 * k) {
            if (i + k < s.size()) {
                reverseString(s, i, i + k - 1);
            } else {
                reverseString(s, i, s.size() - 1);
            }
        }
        return s;
    }
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

也可以就使用C++库函数reverse实现

for (int i = 0; i < s.size(); i += (2 * k)) {
            if (i + k < s.size()) {
                reverse(s.begin() + i, s.begin() + i + k);
            } else {
                reverse(s.begin() + i, s.end());
            }
        }
        return s;
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

注意,C++库函数里面一般也都是左闭右开区间。

剑指Offer 05.替换空格

https://leetcode.cn/problems/ti-huan-kong-ge-lcof/

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

示例 1:

输入:s = "We are happy."
输出:"We%20are%20happy."

思考:双指针,先统计空格数量,然后计算扩充之后的大小。最后使用双指针进行遍历。

class Solution {
public:
    string replaceSpace(string s) {
        int count = 0;
        int oldsize = s.size();
        for (int i = 0; i < s.size(); i++) {
            if (s[i] == ' ') count++;
        }
        int length = s.size() + 2 * count;
        s.resize(length);
        for (int i = length - 1, j = oldsize - 1; i > j; j--, i--) {
            if (s[j] == ' ') {
                s[i--] = '0';
                s[i--] = '2';
                s[i] = '%';
            } else {
                s[i] = s[j];
            }
        }
        return s;
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

随想录代码:

class Solution {
public:
    string replaceSpace(string s) {
        int count = 0; // 统计空格的个数
        int sOldSize = s.size();
        for (int i = 0; i < s.size(); i++) {
            if (s[i] == ' ') {
                count++;
            }
        }
        // 扩充字符串s的大小,也就是每个空格替换成"%20"之后的大小
        s.resize(s.size() + count * 2);
        int sNewSize = s.size();
        // 从后先前将空格替换为"%20"
        for (int i = sNewSize - 1, j = sOldSize - 1; j < i; i--, j--) {
            if (s[j] != ' ') {
                s[i] = s[j];
            } else {
                s[i] = '0';
                s[i - 1] = '2';
                s[i - 2] = '%';
                i -= 2;
            }
        }
        return s;
    }
};

151.翻转字符串里的单词

https://leetcode.cn/problems/reverse-words-in-a-string/

给你一个字符串 s ,请你反转字符串中 单词 的顺序。

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。

**注意:**输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。

示例 1:

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

思考:字符串开头或者结尾的空格,以及中间的多个空格应该舍弃掉。将整个字符串反转,再将每个单词反装。

class Solution {
public:
		//反转,左闭右闭区间
    void reverse(string& s, int start, int end) {
        char temp;
        for (int i = start, j = end; i < j; i++, j--) {
            temp = s[i];
            s[i] = s[j];
            s[j] = temp;
        }
    }

		//移除空格
    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 (i == s.size() || s[i] == ' ') {
                reverse(s, start, i - 1);
                start = i + 1;
            }
        }
        return s;
    }
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(1) 或 O(n),取决于语言中字符串是否可变

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

https://leetcode.cn/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/

字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。

示例 1:

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

思考:

思路一:先旋转前k个字符,再旋转后k个字符,最后再旋转整个字符串。

思路二:用一个额外的字符串,记录前k个字符。之后先将后面的字符串利用双指针法前移,最后再将k个字符串写到末尾。

class Solution {
public:
    string reverseLeftWords(string s, int n) {
        reverse(s.begin(), s.begin() + n);
        reverse(s.begin() + n, s.end());
        reverse(s.begin(), s.end());
        return s;
    }
};
  • 时间复杂度: O(n)
  • 空间复杂度:O(1)

利用额外空间去做的。

class Solution {
public:
    string reverseLeftWords(string s, int n) {
        string s1 = s;
        for (int i = 0; i < n; i++) {
            s1[s.size() - n + i] = s[i];
        }
        for (int i = n, j = 0; i < s.size(); i++) {
            s1[j++] = s[i];
        }
        return s1;
    }
};
  • 时间复杂度: O(n)
  • 空间复杂度:O(1)

复盘总结

个人反思

1、字符串的操作经常需要使用到双指针和反转的思想。

2、还是需要记住一些库函数的。

常见的字符串操作函数总结:(参考:https://blog.csdn.net/Cyril_KI/article/details/106165976)

1、构造函数:

string str:空串,string s(str)复制str到s,string s(num,c)生成由num个c字符构成的字符串。

2、插入函数:

有两种push_back()和insert(),后者比较常用。
push_back():尾部插入一个字符,例如s.push_back(‘p’);
insert():例如s=“abcddss”,执行s.insert(s.begin(),‘i’)后在位置0前插入’i’变成"iabcddss"。

3、遍历:

借助迭代器或者下标,下标比较常用。迭代器分两种:正向和反向。
正向:string::iterator it=s.begin();it!=s.end();it++;
反向:string::reverse_iterator it=s.rebegin();it!=s.rend();it++;
其中*it表示该位置的字符。下标与字符数组一样,不再赘述.

4、查找:

1)、查找一个字符串:s.find(ss),找到返回ss在s中的起始位置,否则返回-1;
2)、从某一个位置开始查找某个字符:s.find(‘t’,6),从s的位置6开始查找字符’t’,找到则返回位置,否则返回-1;注意1)和2)都是返回第一次找到的位置;
3)、从末尾开始查找某个字符:s.rfind(‘t’);
4)、从某个位置开始查找第一个不属于s1的字符位置,例如s=“cdcdscds”,s1=“cdcdfff”,s.find_first_not_of(s1)=4,s的位置4字符不属于s1,类似的还有查找第一个属于s1的字符的位置,去掉not就好。

5、排序:

sort(s.bgein(),s.end());

6、分割截取:

有两个函数:substr()与strtok()。
s.substr(2,5)为s从2位置开始的5个字符组成的子串。
strtok()函数原型:char *strtok(char *s,const char *delim)
函数功能:分解字符串为一组字符串,s为要分解的字符串,delim为分隔字符串。

const char *split=",;!";
char *p=strtok(str,split); 
 while(p!=NULL) {   
    cout<<p<<'\n';   
    p=strtok(NULL, split);   
 } 
//按照分隔符",:!"来分割字符串str  
 return 0;

7、删除:

erase()函数,例如s=“12345678”,s.erase(s.begin()+3)后变为"1235678",即把位置3的字符删掉。

8、替换:

repalce()函数:s.replace(pos,len,ss),将s从pos开始的len个字符替换成ss,

s.replace(pos,n1,n2,c),将s从pos开始的n1个字符替换成n2个字符c

9、大小写转换:

tolower()和toupper()。循环遍历字符串的每一个位置s[i]=tolower(s[i]),大写转小写,或者s[i]=toupper(s[i]),小写转大写。
这里其实推荐用STL中的transform()函数,具体用法如下:
transform(s.begin(),s.end(),s.begin(),::tolower)大写转小写,小写转大写类似。

10、比较函数:

string支持<,>,<=, >=, ==, != 等操作。

compare()函数专门用做string的比较函数,s.compare(ss)=0说明ss == s,返回1表明s>ss,返回-1表明s<ss。

11、拼接函数:

c++的string支持直接相加,“cdd”+“sss”=“cddsss”。另外s.append(ss)表示在s最后加上ss,等同于s+=ss。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值