代码随想录day08|344.反转字符串、541.反转字符串Ⅱ、替换数字

提示:DDU,供自己复习使用。欢迎大家前来讨论~

文章目录

  • 字符串Part01
  • 一、基础知识学习
  • 二、题目
    • 题目一:[344.反字符串](https://leetcode.cn/problems/reverse-string/)
      • 思路:
    • 题目二: [541.反转字符串II](https://leetcode.cn/problems/reverse-string-ii/)
      • 解题思路:
    • 题目三:替换数字
      • 解题思路:
  • 总结


字符串Part01

学习字符串的基础操作。

一、基础知识学习

时间复杂度的计算: 执行次数最多的代码。

下面是两个计算的例子:

  • 一层循环
    解题思路:
    1.循环趟数t和每轮循环i的变化值
    2.t与i的关系
    3.循环停止条件
    4.联立解方程
    5.得到结果

    i = n*n
    while(i!=1){
     	i=i/2
    }
    

    t = log2 (n)

  • 两层循环

    1.写出外层循环中i的变化值

    2.写出内层语句的执行次数(利用求和公式(2+4+6+…+2n))也就是O(n^2)

    for(i=1;i<=n;i++){
    	for(j=1;j<=2*i;j++){
    		m++;
    	}
    }
    

二、题目

题目一:344.反字符串

思路:

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

使用双指针法,依次交换就可以了。(不要使用库函数reverse)

细节:

如果库函数仅仅是 解题过程中的一小部分,并且已经很清楚这个库函数的内部实现原理的话,可以考虑使用库函数。

字符串也是一种数组,所以元素在内存中是连续分布,这就决定了反转链表和反转字符串方式上还是有所差异的。字符串的反转,要比链表简单一些。

class Solution {
public:
    void reverseString(vector<char>& s) {
        for (int i = 0, j = s.size() - 1; i < s.size() / 2; i++, j--) {
            swap(s[i], s[j]); //这里使用了函数,也可以使用中间变量进行两个数的交换
        }
    }
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

题目二: 541.反转字符串II

解题思路:

​ 1.不需要i++,而是使用i += 2k。剪枝操作。

​ 2.剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符。

​ 3.剩余字符少于 k 个,则将剩余字符全部反转。

class Solution {
public:
    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.begin() + i, s.begin() + i + k);
            } else {
                // 3. 剩余字符少于 k 个,则将剩余字符全部反转。
                // reverse接收的是一个迭代器
                reverse(s.begin() + i, s.end());
            }
        }
        return s;
    }
};

题目三:替换数字

题目链接:

解题思路:

  1. 预处理与扩展: 首先,我们对原始数组进行预处理,计算出替换后所需的新数组大小。这是通过将每个数字字符转换为"number"这一特定字符串的长度来实现的。随后,我们扩充原始数组,确保它有足够的空间来容纳替换后的所有元素。
  2. 逆序填充: 接下来,我们采用逆序策略,从数组的末尾开始向前填充。这种方法避免了在填充过程中需要不断移动后续元素的低效操作。通过从后向前逐步替换,我们可以确保每个数字字符都被正确且高效地转换为"number"。

细节:

  • 字符串和数组是一样的,在内存中是连续存在的。

  • 在==数组填充==问题中,选择从后向前填充而非从前向后,是一种优化策略,它能够显著提高算法的效率。

​ 从前向后填充就是O( n 2 n^2 n2)的算法了,因为每次添加元素都要将添加元素之后的所有元素整体向后移动。

​ 其实很多数组填充类的问题,其做法都是先预先给数组扩容带填充后的大小,然后在从后向前进行操作。

这么做有两个好处:

  1. 不用申请新数组。
  2. 从后向前填充元素,避免了从前向后填充元素时,每次添加元素都要将添加元素之后的所有元素向后移动的问题。
#include 
using namespace std;
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(1)

总结

  • 字符串的基础题目,尽量不是关键的步骤下才可以使用库函数。
  • 巩固双指针的操作。
  • 逆序填充,避免增加时间复杂度。

你可能感兴趣的:(算法,c++,leetcode)