344. 反转字符串

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

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

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

示例 1:

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

示例 2:

1
2
输入:s = ["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]

提示:

  • 1 <= s.length <= 105
  • s[i] 都是 ASCII 码表中的可打印字符

思路零

  1. 使用python内置库函数,可以了解下

代码

1
2
3
4
5
6
7
8
9
class Solution:
def reverseString(self, s: List[str]) -> None:
"""
Do not return anything, modify s in-place instead.
"""
# method1
s[::] = s[::-1]
# method2
# s[::] = reversed(s)

思路一双指针

  1. 使用双指针,一个从头开始一个从尾部开始,逐个交换即可

代码

  • 时间复杂度O(N)
  • 空间复杂度O(1)
1
2
3
4
5
6
7
8
9
10
11
12
class Solution:
def reverseString(self, s: List[str]) -> None:
"""
Do not return anything, modify s in-place instead.
"""
L = len(s)
left = 0
right = L - 1
while left < right:
s[left], s[right] = s[right], s[left]
left += 1
right -= 1

思路二用range

  1. 因为while每次都要判断条件,增加了时间复杂度,可以使用range(0, L//2)

代码

  • 时间复杂度O(N)
  • 空间复杂度O(1)
1
2
3
4
5
6
7
8
class Solution:
def reverseString(self, s: List[str]) -> None:
"""
Do not return anything, modify s in-place instead.
"""
L = len(s)
for i in range(L//2):
s[i], s[L - i - 1] = s[L - i -1], s[i]

思路三用栈

  1. 将数据入栈,再逐个弹出;缺点是需要两个循环

代码

  • 时间复杂度O(N)
  • 空间复杂度O(1)
1
2
3
4
5
6
7
8
9
10
11
class Solution:
def reverseString(self, s: List[str]) -> None:
"""
Do not return anything, modify s in-place instead.
"""
L = len(s)
stack = []
for i in s:
stack.append(i)
for i in range(L):
s[i] = stack.pop()

541. 反转字符串 II

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

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

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

示例 1:

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

示例 2:

1
2
输入:s = "abcd", k = 2
输出:"bacd"

提示:

  • 1 <= s.length <= 104
  • s 仅由小写英文组成
  • 1 <= k <= 104

思路

  1. 输入是字符串,必须经过处理转成列表;字符串无法被直接修改
  2. 使用反转字符串的思路,写个完全反转的辅助函数
  3. 主思路,使用两个指针,每次按2k的长度对s进行滑动(所谓滑动窗口)
    1. 对窗口内的子段进行长度判断,如果长度大于等于k,则只反转前k个;否则反转窗口内所有内容
  4. 更新left与right;其中left = right;而right = left + 2 * k;

代码

  • 时间复杂度O(N)
  • 空间复杂度O(1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution:
def reverseStrClip(self, s:list) -> str:
L = len(s)
for i in range(L//2):
s[i], s[L - i - 1] = s[L - i -1], s[i]
return s

def reverseStr(self, s: str, k: int) -> str:
# 字符串无法被修改,必须转成list
s = list(s)
left = right = 0
L = len(s)
# 退出条件是left到底
while left < L:
left = right
right = left + 2 * k
if right - left >= k:
s[left: left+k] = self.reverseStrClip(s[left: left+k])
else:
# 因为剩下所有的内容已经小于k了,全部反转即可
s[left:] = self.reverseStrClip(s[left:])
#s[left: right] = self.reverseStrClip(s[left: right])
# 转回字符串
return "".join(s)

改进,同样的,推荐使用range而不是while,可以提高速度

Range(0, L, 2 * k),即将i作为left,而right每次手动计算下即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution:
def reverseStrClip(self, s:list) -> str:
L = len(s)
for i in range(L//2):
s[i], s[L - i - 1] = s[L - i -1], s[i]
return s

def reverseStr(self, s: str, k: int) -> str:
# 设置好开始结束区间
# 一共两个条件
s = list(s)
left = right = 0
# 退出条件是left到底
L = len(s)
for i in range(0, L , 2 * k):
if i + k <= L:
s[i: i + k] = self.reverseStrClip(s[i: i + k])
else:
# 剩下内容个数小于k,全部反转
# s[i:] = self.reverseStrClip(s[i:]) 也是可以的
s[i: i + 2 * k] = self.reverseStrClip(s[i: i + 2 * k])
return "".join(s)

思路二(Python列表特性语法糖)

Python列表特性语法糖

  1. 更直接一点儿,都不用进行条件判断,直接对每次2k内的前k个元素进行反转即可(不用管它长度是大于k还是小于k,直接按k个进行切片)
  2. 这里利用了python的一个列表性质:s[start:end],哪怕end越出了实际长度,python只会把它当成s[start:-1],也就是把start后所有内容返回,而不会报错;
    • 比如s = [‘a’, ‘b’] 且k = 4,如果i = 0时,s[0 + 4]会是[‘a’, ‘b’]
  3. 利用这种性质,可以直接s[i: i + k] = self.reverseStrClip(s[i: i + k]) ,而不用进行长度的判断;这也算是语法糖了,这种东西最好不要用,不然容易解释不清楚,还以为这样的算法是正确的,但没想到是语言在兜底

代码

  • 时间复杂度O(N)
  • 空间复杂度O(N)
1
2
3
4
5
6
7
8
9
10
class Solution:
def reverseStr(self, s: str, k: int) -> str:
L = len(s)
s = list(s)
for i in range(0, L, 2 * k):
#
s[i: i + k] = s[i: i + k][::-1]
# 或者用自己写的函数也行
# s[i: i + k] = self.reverseStrClip(s[i: i + k])
return "".join(s)

54.替换数字

https://kamacoder.com/problempage.php?pid=1064

题目描述

给定一个字符串 s,它包含小写字母和数字字符,请编写一个函数,将字符串中的字母字符保持不变,而将每个数字字符替换为number。 例如,对于输入字符串 “a1b2c3”,函数应该将其转换为 “anumberbnumbercnumber”。

输入描述

输入一个字符串 s,s 仅包含小写字母和数字字符。

输出描述

打印一个新的字符串,其中每个数字字符都被替换为了number

输入示例

1
a1b2c3

输出示例

1
anumberbnumbercnumber

提示信息

数据范围:
1 <= s.length < 10000。

思路

  1. 偷懒思路:字符串转成列表,再判断是否为数字,是则替换
  2. 可以使用ord()来找ascii码,比较通用;或者使用python库函数s.isdigit()

代码

1
2
3
4
5
6
7
8
9
10
def foo(s:str) -> str:
s = list(s)
L = len(s)
for i in range(L):
if ord('9') >= ord(s[i]) >= ord('0'):
s[i] = "number"
return "".join(s)

s = input()
print(foo(s))

思路二(纯算法)

  1. 如果需要正常写,需要进行 数组扩展 以及 数组填充(倒序填充)
  2. 先统计原数组内的digital个数,再创建个新数组res,容量为len(s) + countDig * 5
  3. 随后,使用双指针,同时对s与res从后向前遍历,如果遇到s[indexOld]是digital则将res[indexNew-5: indexNew+1]="number"直接赋值即可,或者逐个赋值;如果不是digital,直接将s[indexOld]复制到res[indexNew]即可

img

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class solution():
def foo(self, s: str) -> str:
L = len(s)
# 统计str中digital个数
countDigital = sum(1 for i in s if i.isdigit())
# 创建新结果数组, 等于原数组长度 + countDigital * 5
res = [''] * (countDigital * 5 + L)
# 从后向前对res进行填充
newIndex = len(res) - 1
oldIndex = L - 1
while oldIndex >= 0:
if s[oldIndex].isdigit():
res[newIndex - 5: newIndex + 1] = "number"
newIndex -= 6
else:
res[newIndex] = s[oldIndex]
newIndex -= 1
oldIndex -= 1

return "".join(res)

s = input()
solu = solution()
print(solu.foo(s))