Leetcode刷题:剑指offer【面试题05 替换空格】

文章目录

  • 方案一:使用 python
  • 方案二:使用 C++ ⭐

【面试题05】替换空格

难度: 简单
限制: 0 <= s 的长度 <= 10000

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

Leetcode题目对应位置: 面试题05:替换空格

方案一:使用 python

(python 写的话没有还原到原题目考点的精髓,建议使用 C++)

在 Python 语言中,字符串是不可变类型,即无法修改字符串中的某一位字符,所以用 Python 语言写的话,需要新建一个字符串来实现题目要求。

啰嗦一下,python 字符串不可变的意思是你不能用 a[0]='p' 这种方式对字符串进行改变,而 a = 'dadsa' 这种方式实际是重新赋值,重新赋值是允许的。

代码逻辑:
1)创建一个新列表,t;
2)循环遍历字符串 s 中的每个字符 c,若 c 是空格,则向列表 t 中添加字符串 “%20”;否则向列表 t 中添加字符 c;
3)将列表 t 转化为字符串并返回。

时间复杂度:O(n),需要遍历整个长度为 n 的字符串 s。
空间复杂度:O(n),需要额外的线性辅助空间。

# python
class Solution:
    def replaceSpace(self, s: str) -> str:
        length = len(s)
        new = [0] * (len(s) * 3)
        size = 0
        for ss in s:
            if ss is ' ':
                new[size] = '%'
                new[size+1] = '2'
                new[size+2] = '0'
                size += 3
            else: 
                new[size] = ss
                size += 1
        return ''.join(new[:size])

在这里插入图片描述
看到别的大佬有更简洁的做法:

# 参考1
class Solution:
    def replaceSpace(self, s: str) -> str:
        res = []
        for c in s:
            if c == ' ': res.append("%20")
            else: res.append(c)
        return "".join(res)

# 参考2
class Solution:
    def replaceSpace(self, s: str) -> str:
        return ''.join(('%20' if c==' ' else c for c in s))

在这篇解答里也提到了另一种连接字符串的方法,就是用 +

# 参考3
class Solution:
    def replaceSpace(self, s: str) -> str:
        res = ""
        for c in s:
            if c == ' ': res += "%20"
            else: res += c
        return res

但是当连接次数过多时,使用 + 的方法性能会急剧下降,参考文章:你所不知道的Python | 字符串连接的秘密,所以比较推荐的是 .join 方法。

方案二:使用 C++ ⭐

在剑指 offfer 原书中,这道题实际上是想考察 对内存覆盖的理解分析时间效率 的能力。假设保证输入的字符串后面有足够多的空余内存,要求在原本的字符串上进行替换。这里有两种思路:第 1 个是从前往后遍历替换,第 2 个是从后往前遍历替换(第二种才是比较推荐的巧妙的解法)。

官方代码: 05_ReplaceSpaces/ReplaceSpaces.cpp(第二种方法)

思路一: 从前往后遍历替换

从前往后遍历字符串,每遇到一个空格就将其后的所有字符后移两位。

时间复杂度:O(n^2),如果字符串长度为 n,对于每个空格字符都需要移动其后面的 O(n) 个字符
空间复杂度:O(1)

需要注意的细节: 当遇到空格字符后,会由原来的 1 个空格字符替换为 3 个字符,所以遍历字符串的指针 i i i 需要对应 +2,才能继续指向原字符串的下一个字符,否则会指向字符 “2”。

#include 
#include 

void ReplaceBlank(char str[], int length)
{
    if (str == nullptr && length <= 0)
        return;

    /*计算字符串实际长度*/
    int oriLength = 0;
    while (str[oriLength] != '\0')
    {
    	oriLength++;
    }

    /*从左向右替换字符串中的空格*/
    int count = 0;  // 用于记录当前是第几个空格
    for (int i = 0; i < oriLength; i++)
    {   
        int idx = i + 2 * count;
        if (str[idx] == ' ')
        {
            /*将空格后的字符依次后移两位*/
            for (int j = oriLength + 2 * count; j > idx; j--)
            {
                str[j + 2] = str[j];
            }
            str[idx] = '%';
            str[idx + 1] = '2';
            str[idx + 2] = '0';
            count++;
        }
    }
}

思路二: 从后往前遍历替换

自己写的代码逻辑:

1)先遍历一遍字符串,统计出字符串的实际长度和空格字符的个数;
2)从后向前遍历字符串,每遇到一个字符,计算该字符应该移动到的位置,也就是 new index,new index = 字符串实际长度 + 尚未遇到的空格字符个数 * 2,需要注意的是,如果遇到的是空格字符,则尚未遇到的空格字符数需要减 1;
3)设置一个额外变量 count,用于记录当前还剩下几个空格没有遇到,且每遇到一个空格 count - 1。

#include 
#include 

void ReplaceBlank(char str[], int length)
{
    if (str == nullptr && length <= 0)
        return;

    /*统计字符串的实际长度oriLength
      及空格个数spacNum*/
    int i = 0;
    int oriLength = 0;
    int spacNum = 0;
    while (str[i] != '\0')
    {
        oriLength++;
        if (str[i] == ' ')
        {
            spacNum++;
        }
        i++;
    }
    
    /*判断是否会越界*/
    int newLength = oriLength + spacNum * 2;
    if (newLength > length)
        return;

    /*从后往前遍历字符串*/
    int count = spacNum;
    int newId = 0;
    for (i = oriLength; i >= 0; i--)
    {
        if (str[i] == ' ')
        {
            newId = i + (count - 1) * 2;  // 注意这里尚未遇到的空格字符要减1
            str[newId] = '%';
            str[newId+1] = '2';
            str[newId+2] = '0';
            count--;
        }
        else
        {
            newId = i + count * 2;
            str[newId] = str[i];
        }
    }
}

官方给出的代码逻辑 ⭐:

1)定义两个指针p1、p2,初始时 p1 指向原字符串的末尾,p2 指向置换后字符串的末尾;
2)向前移动指针 p1,将它指向的字符复制到 p2 所指向的位置,直到碰到第一个空格字符为止;
3)碰到第一个空格字符后,将 p1 向前移动 1 步,在 p2 位置上放字符 “0”,p2 继续向前移动了 2 步,这两步上分别放 “2” 和 “%”,然后再将 p2 向前移动 1 步,这时 p1 又指向了一个不为空格字符的字符,p2 仍是其对应的新插入位置;
4)当 p1 和 p2 指向同一位置时,说明所有空格都已经替换完毕,直接退出。

这个方法灵性就灵性在于,p1 每次只走 1 步,而 p2 在 p1 指向非空格字符时走 1 步,在 p1 指向空格字符时走 3 步,所以假设有 m 个空格字符,那么 p2 与 p1 之间就相差 2m 步,每遇到一个空格,p2 与 p1 之间的差距就减少 2 步,所以在遇到第 m 个空格后,p2 与 p1 之间的差距恰好为 0,也就是 p2 恰好追上了 p1。比自己写的代码优就优在省去了当前面不再有空格字符时的判断,学习了!

#include 
#include 

/*length 为字符数组str的总容量,大于或等于字符串str的实际长度*/
void ReplaceBlank(char str[], int length)
{
    if(str == nullptr && length <= 0)
        return;

    /*originalLength 为字符串str的实际长度*/
    int originalLength = 0;
    int numberOfBlank = 0;
    int i = 0;
    while(str[i] != '\0')
    {
        ++ originalLength;

        if(str[i] == ' ')
            ++ numberOfBlank;

        ++ i;
    }

    /*newLength 为把空格替换成'%20'之后的长度*/
    int newLength = originalLength + numberOfBlank * 2;
    if(newLength > length)
        return;

    int indexOfOriginal = originalLength;
    int indexOfNew = newLength;
    while(indexOfOriginal >= 0 && indexOfNew > indexOfOriginal)
    {
        if(str[indexOfOriginal] == ' ')
        {
            str[indexOfNew --] = '0';
            str[indexOfNew --] = '2';
            str[indexOfNew --] = '%';
        }
        else
        {
            str[indexOfNew --] = str[indexOfOriginal];
        }

        -- indexOfOriginal;
    }
}

在 LeetCode 运行:

//C++
class Solution {
public:
    string replaceSpace(string s) {
        if(s.length()==0) 
        {
            return s;
        }

        int oriLength = s.size() - 1;
        int spacNum = 0;
        
        /*统计空格字符个数*/
        for(auto c:s)
        {
            if(c == ' ') 
            {
                ++spacNum;
            }
        }

        int newLength = oriLength + spacNum * 2;
        s += string(spacNum * 2,' ');  // 这里为了保证有足够的空间容纳新字符串
        /*从后往前遍历字符串*/
        while(oriLength >= 0 && newLength > oriLength)
        {
            if(s[oriLength] == ' '){
                s[newLength--] = '0';
                s[newLength--] = '2';
                s[newLength--] = '%';
            }
            else{
                s[newLength--] = s[oriLength];
            }
            oriLength--;
        }
        return s;
    }
};

在这里插入图片描述


参考资料:
[1] LeetCode 面试题05:替换空格
[2] 参考题解 - 作者:Krahets
[3] 你所不知道的Python | 字符串连接的秘密
[4] 剑指 offer 第二版

你可能感兴趣的:(今天刷题了吗)