2024/12/12 【字符串】LeetCode541. 反转字符串 II 【√】知识点:字符串是不可变类型,及其与切片的关系,理解[ : : -1]
题意理解需要花点时间,与昨天的344. 反转字符串 - 力扣(LeetCode)不同在于,昨天题目的字符串是字符列表类型,今天的字符串是双括号括起来的str类型。
这样造成了一些处理上的不同:需要考虑到str类型是不可变类型,对其进行处理,需要转为list类型,返回结果的时候,需要再把字符的list列表,通过‘’.join(list_name)方法把字符拼接成字符串。
我的解答:
class Solution: def reverseStr(self, s: str, k: int) -> str: n = len(s) rem = n % 2*k cycles = n // 2*k s = list(s) #循环处理每个2k范围内的前k个字符 for i in range(cycles): s[i*2*k : i*2*k+k] = reversed(s[i*2*k : i*2*k+k]) #处理剩余的不足以构成一个2k范围区间的字符 if rem < 2*k and rem >= k: s[cycles*2*k : cycles*2*k+k] = reversed(s[cycles*2*k : cycles*2*k+k]) elif rem < k: s[cycles*2*k : cycles*2*k+k] = reversed(s[cycles*2*k : cycles*2*k+k]) s = ''.join(s) return s
代码随想录提供的代码:
版本1:同样的思路,但是灵活应用range(start,end,step)更加简洁
class Solution: def reverseStr(self, s: str, k: int) -> str: n = len(s) res = list(s) for cur in range(0, n, 2*k): res[cur:cur+k] = res[cur:cur+k][::-1] return ''.join(res)
版本2:
class Solution: def reverseStr(self, s: str, k: int) -> str: p = 0 while p < len(s): p2 = p + k s = s[:p] + s[p:p2][::-1] + s[p2:] p += 2*k return s
1.
s = list(s)
的必要性:
字符串是不可变类型(immutable)。这意味着不能直接修改字符串中的某个字符或者通过切片来替换它。
错误代码:
s[:k] = reversed(s)
错误分析:
代码中的 s[:k] = reversed(s)
试图将字符串 s
中的前 k
个字符替换为字符串 s
的反转(reversed(s)
返回的是一个反转的可迭代对象)。但是,字符串不能像列表一样直接进行切片赋值,因此会抛出这个错误。
示例:
s = "hello" s[:3] = "abc" # 会报错,因为字符串是不可变的
正确的做法:
如果你希望对字符串的某一部分进行替换,可以先将字符串转换为列表(列表是可变的),然后进行操作。完成操作后,再将列表转换回字符串。
修改方法:
s = "hello" k = 3 # 先将字符串转为列表 s_list = list(s) # 反转字符串后取前k个字符 s_list[:k] = list(reversed(s)) # 将列表再转回字符串 s = ''.join(s_list) print(s)
在这段代码中:
list(s)
将字符串转换为一个列表。reversed(s)
返回一个反转的可迭代对象,所以通过list(reversed(s))
可以获得一个反转后的列表。- 最后,使用
''.join(s_list)
将列表重新组合成一个字符串。
2.
''.join(s_list)
''.join(s_list)
是一个用于将列表 s_list
中的元素合并成一个字符串的方法。它的作用是连接列表中的所有字符串元素,生成一个新的单一字符串。
详细解释:
-
''.join(iterable)
:这是str
类型的一个方法,用于将一个可迭代对象(如列表、元组等)中的所有元素连接成一个新的字符串。连接时,可以指定一个分隔符(通过''
),即在每个元素之间插入的字符串。iterable
:可以是任何可迭代对象(如列表、元组、字符串等)。在此情况下,它是一个字符串列表s_list
。''
:表示连接时没有任何分隔符。也就是说,所有元素将被直接连接起来。
示例:拼接字符串的效率对比
# 使用 '+' 拼接字符串 result = "" for word in ['H', 'e', 'l', 'l', 'o']: result += word # 每次拼接都会创建一个新的字符串 print(result) # 输出:Hello # 使用 ''.join 拼接字符串 result = ''.join(['H', 'e', 'l', 'l', 'o']) # 只会创建一个新字符串 print(result) # 输出:Hello
使用 join
方法在多个字符串拼接时效率较高,因为它会在一个操作中完成所有的拼接,而使用 +
会创建多个临时字符串,导致更多的内存开销。
总结来说,''.join(s_list)
是一个高效且常用的将列表中的元素合并为字符串的方法,特别适用于字符串拼接和字符串重组操作。
3. 理解s[p: p2][::-1]
s[p: p2][::-1]
是 Python 中常见的切片操作与反向切片的组合。
分解解释
-
s[p: p2]
:- 这里对字符串
s
进行切片操作。 p
是起始索引,p2
是结束索引(不包括p2
处的字符)。- 它会返回一个从索引
p
到p2-1
的子字符串。
- 这里对字符串
-
[::-1]
:- 这是反向切片操作,用于将字符串或列表反转。
- 其中
:
表示取整个字符串(或列表),-1
表示步长为 -1,意味着从末尾向前进行切片。
示例:
s = "abcdefg" print(s[::-1]) # 输出:'gfedcba',将整个字符串反转
-
组合
s[p: p2][::-1]
:s[p: p2]
先提取字符串的子串。[:: -1]
再将这个子串反转。
示例:
s = "abcdefg" p = 1 p2 = 5 result = s[p: p2][::-1] print(result) # 输出:'edcb'
总结
s[p: p2]
提取字符串s
的一部分(从索引p
到p2-1
)。[:: -1]
将提取的部分反转。- 整个表达式
s[p: p2][::-1]
就是提取一个子串并反转。
这个组合操作在需要对字符串的某一部分进行反向处理时非常常用。
4. 字符串是不可变类型,及其与切片的关系
字符串之所以被设计成不可变类型(immutable),是出于以下几个原因:
(1). 内存管理和性能优化
- 不可变对象 可以被多个引用共享,避免重复创建,节省内存。
- 在 Python 中,字符串常被用作键(比如字典中的键),不可变性使字符串具有哈希值,从而可以快速检索和比较。
示例:
a = "hello" b = "hello" print(id(a), id(b)) # 输出相同的内存地址,表明 a 和 b 指向同一个对象
- 字符串是不可变的,因此
"hello"
不会因为某个变量的操作而被修改,多个引用也无需担心意外变化。
(2). 保证数据安全
- 由于不可变,字符串在多线程或共享资源的环境中更安全,不会因为某个操作导致意外修改。
(3). 字符串的操作返回新对象,而非修改原对象
- 任何对字符串进行“修改”的操作(比如拼接、切片等)都会返回一个新字符串对象,而原字符串保持不变。
示例:
s = "hello" s_new = s + " world" print(s) # 输出:hello print(s_new) # 输出:hello world
- 原字符串
s
没有改变,操作后的结果存储在新的变量s_new
中。
(4). 切片操作和不可变性的关系
当你对字符串进行切片操作时,实际上 不会修改原字符串,而是创建了一个新字符串对象,并返回这个新对象。
示例:
s = "abcdef" s2 = s[1:4] # 切片操作,提取索引 1 到 3 的子串 print(s2) # 输出:'bcd' print(s) # 原字符串 'abcdef' 没有变化
s[1:4]
返回一个新的字符串'bcd'
,而s
依旧是'abcdef'
。- 原因是字符串是不可变的,Python 不会修改 原字符串,而是生成一个新的字符串对象。
总结
- 字符串是不可变类型,意味着字符串对象一旦创建,其内容无法被修改。
- 但这并不意味着不能进行操作。对字符串的任何“修改”操作(例如切片、拼接、替换等)都会返回一个新对象,而不会改变原字符串本身。
- 切片操作之所以能执行,是因为它只是提取出部分内容,并返回一个新的字符串对象。