字符串经典题目
反转的问题
LeetCode344、541、917、151、557
反转字符串
LeetCode344. 反转字符串
https://leetcode.cn/problems/reverse-string/
思路分析
最基本的反转题,使用双指针进行反转
反转前字符数组
s[0],s[1],s[2] … s[n-1]
反转后字符数组
s[n-1],s[n-2],s[n-3] … s[0]
=> s[i]和s[j]交换,i+j = n-1 => s[i]和s[n-i-1]交换
双指针解法:
- left指向字符数组首元素,right指向字符数组尾元素
- left<right时
交换s[left]和s[right]
left右移一位
right左移一位 - left>=right,反转结束
代码实现
class Solution:
def reverseString(self, s: List[str]) -> None:
"""
Do not return anything, modify s in-place instead.
"""
if not s or len(s)<=1:
return s
left, right = 0, len(s)-1
while left < right:
s[left], s[right] = s[right], s[left]
left += 1
right -= 1
return s
k个一组反转
LeetCode541. 反转字符串 II
https://leetcode.cn/problems/reverse-string-ii/
思路分析
将需要反转的子字符串拆分处理,每个子字符串的开始下标为2kn(n=0,1,…),长度为k,即 [ 2kn, 2k*n+k]
反转每个下标从2k的倍数开始的,长度为k的子串;若该子串长度不足k,则反转整个子串
代码实现
class Solution:
def reverseStr(self, s: str, k: int) -> str:
if not s or len(s) <= 1:
return s
char_list = list(s)
for i in range(0, len(char_list), 2*k):
char_list[i:i+k] = reversed(char_list[i:i+k])
return "".join(char_list)
class Solution:
def reverseStr(self, s: str, k: int) -> str:
def reverse_arr(arr):
if not arr or len(arr) <= 1:
return arr
left, right = 0, len(arr)-1
while left < right:
arr[left], arr[right] = arr[right], arr[left]
left += 1
right -= 1
return arr
if not s or len(s) <= 1:
return s
char_list = list(s)
for i in range(0, len(char_list), 2*k):
char_list[i:i+k] = reverse_arr(char_list[i:i+k])
return "".join(char_list)
仅仅反转字母
LeetCode917. 仅仅反转字母
https://leetcode.cn/problems/reverse-only-letters/
思路分析
方法1:使用栈
将s中的所有字母单独存入栈中,出栈等于对字母反序操作(或者数组存储,并反序数组)
遍历s的所有字符,如果是字母我们选择栈顶元素输出
方法2:双指针
一个接一个输出s的所有字符,当遇到一个字母时,我们希望找到逆序遍历字符串的下一个字母
所以我们这么做:维护一个指针 j 从后往前遍历字符串,当需要字母时就使用它
代码实现
方法1:使用栈
class Solution:
def reverseOnlyLetters(self, s: str) -> str:
# 方法1:使用栈
if not s or len(s) <= 1:
return s
# 收集字符串中所有的字母
letters = []
for i in s:
if i.isalpha():
letters.append(i)
# 反转字符串中字母
char_list = list(s)
for i in range(0, len(char_list)):
if char_list[i].isalpha():
char_list[i] = letters.pop()
return "".join(char_list)
class Solution:
def reverseOnlyLetters(self, s: str) -> str:
# 方法1:使用栈
letters = []
for c in s:
if c.isalpha():
letters.append(c)
ans = ''
for c in s:
ans += letters.pop() if c.isalpha() else c
return ans
方法2:双指针
class Solution:
def reverseOnlyLetters(self, s: str) -> str:
# 方法2:双指针
char_list = list(s)
n = len(char_list)
left, right = 0, n - 1
while left < right:
while left < n and not char_list[left].isalpha():
left += 1
while right >= 0 and not char_list[right].isalpha():
right -= 1
if left < right:
char_list[left], char_list[right] = char_list[right], char_list[left]
left += 1
right -= 1
return "".join(char_list)
class Solution:
def reverseOnlyLetters(self, s: str) -> str:
# 方法2:双指针
if not s:
return s
ans = ''
j = len(s) - 1
for i in range(len(s)):
if s[i].isalpha():
while not s[j].isalpha():
j -= 1
ans += s[j]
j -= 1
else:
ans += s[i]
return ans
反转字符串里的单词
LeetCode151. 反转字符串中的单词
https://leetcode.cn/problems/reverse-words-in-a-string/
思路分析
方法1:使用语言提供的方法实现
- split(拆分)将字符串按空格分割成字符串数组
- reverse/reversed(反转)将字符串数组进行反转
- join(连接)将字符串数组拼接成一个字符串
其他,strip() 去除开头和末尾的空白字符,注:split()会自动去除开头和末尾的空白字符
print(" 1 2 3 4 5 ".split()) # ['1', '2', '3', '4', '5']
print(" 1 2 3 4 5 ".split(' ')) # ['', '1', '2', '3', '4', '', '', '', '', '5', '', '', '', '', '']
方法2:自己实现上述功能
执行步骤
- 去除空白字符,去除开头、末尾、中间多余(单词之间只保留一个)空白字符
- 反转整个字符串
- 反转每个单词
- 连接单词
代码实现
方法1:使用语言提供的方法实现
class Solution:
def reverseWords(self, s: str) -> str:
words = s.split()
words.reverse()
return " ".join(words) # 精简 return " ".join(reversed(s.split()))
方法2:自己实现上述功能
class Solution:
def trim_space(self, s):
# 去除开头、结尾空白字符,单词之间只保留一个空白字符
left, right = 0, len(s) - 1
# 去除开头空白字符
while s[left] == ' ':
left += 1
# 去除结尾空白字符
while s[right] == ' ':
right -= 1
# 去除字符串间多余空白字符
output = []
while left <= right:
if s[left] != ' ':
output.append(s[left])
elif output[-1] != ' ':
output.append(s[left])
left += 1
return output
def reverse(self, arr: list, left: int, right: int):
while left <= right:
arr[left], arr[right] = arr[right], arr[left]
left += 1
right -= 1
def reverse_each_word(self, arr: list):
n = len(arr)
start = 0
end = 0
while start < n:
# 找到单词的末尾
while end < n and arr[end] != ' ':
end += 1
# 反转单词
self.reverse(arr, start, end - 1)
# 更新start,寻找下一个单词
start = end + 1
end += 1
def reverseWords(self, s: str) -> str:
# 方法2:自己的方法实现
letters = self.trim_space(s)
self.reverse(letters, 0, len(letters) - 1)
self.reverse_each_word(letters)
return "".join(letters)
验证回文串
LeetCode125. 验证回文串
https://leetcode.cn/problems/valid-palindrome/
思路分析
先转换成字符数组,然后使用双指针方法从两头到中间比较
代码实现
class Solution:
def isPalindrome(self, s: str) -> bool:
letters = []
for i in s:
if 'a' <= i <= 'z' or '0' <= i <= '9':
letters.append(i)
elif 'A' <= i <= 'Z':
letters.append(i.lower())
left, right = 0, len(letters) - 1
while left < right:
if letters[left] != letters[right]:
return False
left += 1
right -= 1
return True
代码简化
class Solution:
def isPalindrome(self, s: str) -> bool:
letters = "".join(ch.lower() for ch in s if ch.isalnum())
left, right = 0, len(letters) - 1
while left < right:
if letters[left] != letters[right]:
return False
left += 1
right -= 1
return True
字符串中第一个唯一字符
LeetCode387. 字符串中的第一个唯一字符
https://leetcode.cn/problems/first-unique-character-in-a-string/
提示:
1 <= s.length <= 105
s 只包含小写字母
思路分析
可以对字符串进行两次遍历
第一次遍历,使用哈希映射统计出字符串中每个字符出现的次数
第二次遍历,只要遍历到了只出现一次的字符,就返回它的索引,否则在遍历结束后返回 -1
代码实现
class Solution:
def firstUniqChar(self, s: str) -> int:
frequency = {}
for i in s:
if i in frequency:
frequency[i] += 1
else:
frequency[i] = 1
for i in range(len(s)):
if frequency[s[i]] == 1:
return i
return -1
代码简化
class Solution:
def firstUniqChar(self, s: str) -> int:
frequency = collections.Counter(s)
for i, ch in enumerate(s):
if frequency[ch] == 1:
return i
return -1
判定是否互为字符重排
LeetCode242. 有效的字母异位词
https://leetcode.cn/problems/valid-anagram/
给定两个字符串S1和S2,请编写一个程序确定其中一个字符串的字符重新排列后,能否编程另一个字符串
思路分析
方法1:排序法
将两个字符串全部从小到达或者从大到小排列,然后在逐个位置比较,这时候不管两个原始字符串是什么,都可以判断出来
方法2:使用哈希
如果一个字符串经过重新排列后,能够变成另外一个字符串,那么它们的每个不同字符的出现次数是相同的
代码实现
方法1:排序法
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
if len(s) != len(t):
return False
sorted_s = "".join(sorted(s))
sorted_t = "".join(sorted(t))
return sorted_s == sorted_t
注:python 中 sorted(‘cba’) 执行返回 [‘a’, ‘b’, ‘c’]
方法2:使用哈希
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
frequency_s = collections.Counter(s)
frequency_t = collections.Counter(t)
if sorted(frequency_s.keys()) != sorted(frequency_t.keys()):
return False
for i in frequency_s:
if frequency_s[i] != frequency_t[i]:
return False
return True
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
if len(s) != len(t):
return False
table = [0] * 26
for ch in s:
table[ord(ch) - ord('a')] += 1
for ch in t:
index = ord(ch) - ord('a')
table[index] -= 1
if table[index] < 0:
return False
return True