个人评价:又是一道内置函数 5s 解题(迷惑脸?)但是个人认为还是蛮值得深入的

题目难度:🌟 (参考 LeeCode 简单🌟 、中等🌟 🌟 、困难🌟 🌟 🌟 )

另,通常算法题会考察分析时间复杂度和空间复杂度,可参考笔者之前的文章

「数据结构&算法」必知必会系列:3. 时间 & 空间复杂度(下)

替换空格

题目描述

剑指 Offer 05. 替换空格

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

示例 1:

输入:s = “We are happy.”

输出:“We%20are%20happy.”

限制:

0 <= s 的长度 <= 10000

解题思路

方法一

replace 函数不少语言应该都支持吧?或者正则匹配也成

不管了,直接上 😂

func replaceSpace(s string) string {
    return strings.ReplaceAll(s, " ", "%20")
}

方法二

好吧,好歹也是算法的题目,想想遍历实现以及这题的初衷?

字符串通常被设计成「不可变」的类型(Go、Python、Java),即无法直接修改字符串的某一位字符,需要新建一个字符串实现。

那么,我们通常会处理为:

  • 初始化一个 list
  • 遍历列表 s 中的每个字符 c
  • 当 c 为空格时:向 list 后添加字符串 “%20”
  • 当 c 不为空格时:向 list 后添加字符 c
  • 将列表 list 转化为字符串并返回

复杂度?🙌 时间&空间均为 O(n)

C++ 中,字符串可变,参考先扩展后填充的方式,示例图如下:

题目引申

具体看下 Golang strings.ReplaceAll 的实现吧~

若不考虑 UTF-8编码、+Buffer,基本思路也就是遍历+替换

// Replace returns a copy of the string s with the first n
// non-overlapping instances of old replaced by new.
// If old is empty, it matches at the beginning of the string
// and after each UTF-8 sequence, yielding up to k+1 replacements
// for a k-rune string.
// If n < 0, there is no limit on the number of replacements.
func Replace(s, old, new string, n int) string {
	if old == new || n == 0 {
		return s // avoid allocation
	}

	// Compute number of replacements.
	if m := Count(s, old); m == 0 {
		return s // avoid allocation
	} else if n < 0 || m < n {
		n = m
	}

	// Apply replacements to buffer.
	var b Builder
	b.Grow(len(s) + n*(len(new)-len(old)))
	start := 0
	for i := 0; i < n; i++ {
		j := start
		if len(old) == 0 {
			if i > 0 {
				_, wid := utf8.DecodeRuneInString(s[start:])
				j += wid
			}
		} else {
			j += Index(s[start:], old)
		}
		b.WriteString(s[start:j])
		b.WriteString(new)
		start = j + len(old)
	}
	b.WriteString(s[start:])
	return b.String()
}


// ReplaceAll returns a copy of the string s with all
// non-overlapping instances of old replaced by new.
// If old is empty, it matches at the beginning of the string
// and after each UTF-8 sequence, yielding up to k+1 replacements
// for a k-rune string.
func ReplaceAll(s, old, new string) string {
	return Replace(s, old, new, -1)
}

替换核心就是下面三句

b.WriteString(s[start:j]) //start标记从旧字符串的哪个位置开始
b.WriteString(new) //写入替换数据
start = j + len(old) //移动start

总结

题是不难啦,但有点小启发 🤔

t := make([]byte, len(s)+n*(len(new)-len(old)))

在源码中很是常见,在创建slice的时候,尽可能的去指定好你需要的长度来避免扩容。👏

面试中,可以具体深入问 replace 怎么实现有看过源码么?若没有你会怎么实现呢?<( ̄▽ ̄)/

扩容+Buffer 和 UTF 编码都是值得提及的部分哦 (✧◡✧)