leetcode(19)字符串系列题目
541. 反转字符串 II
class Solution:
def reverseStr(self, s: str, k: int) -> str:
p = 0
while p < len(s):
q = p + k
s = s[:p] + s[p:q][::-1] + s[q:]
p = p + k * 2
return s
剑指 Offer 05. 替换空格
简单粗暴的一些方法:
class Solution:
def replaceSpace(self, s: str) -> str:
# return "%20".join(s.split())
return "%20".join(s.split(' ')) # 等价
s = re.sub(' ','%20',s)
return s
从后往前遍历
class Solution:
def replaceSpace(self, s: str) -> str:
n = len(s)
for k, v in enumerate(s[::-1]):
print(k,v)
if v == ' ':
s = s[:n - k - 1] + '%20' + s[n - k:]
return s
151. 颠倒字符串中的单词
分三步:
1.先翻转整个数组
2.再翻转单个单词
3.清除多余空格
class Solution:
def reverseWords(self, s: str) -> str:
s, n = list(s), len(s)
def reverse(s, i, j):
while i < j:
s[i], s[j] = s[j], s[i]
i += 1
j -= 1
def reverse_w(s):
i = j = 0
while i < n:
while i < n and s[i] == ' ': # 找到单词开头
i += 1
j = i
while j < n and s[j] != ' ': # 找到单词结尾
j += 1
reverse(s, i, j - 1) # 注意是j - 1
i = j
def clean_space(s):
i = j = 0 # 类似于快慢指针
while j < n:
while j < n and s[j] == ' ': # 去除前面的空格
j += 1
while j < n and s[j] != ' ':
s[i] = s[j] # 即把快指针位置的值赋值给慢指针
i += 1
j += 1
while j < n and s[j] == ' ': # 删除中间位置的空格
j += 1
if j < n: # 在单词与单词之间放一个空格
s[i] = ' '
i += 1
return ''.join(s[:i])
reverse(s, 0, n - 1)
reverse_w(s)
return clean_space(s)
直接用spilt()函数,可以但不建议
class Solution:
def reverseWords(self, s: str) -> str:
s_list = s.split()
# print(s_list)
return ' '.join(s_list[::-1])
剑指 Offer 58 - II. 左旋转字符串
偷懒做法用切片:
class Solution:
def reverseLeftWords(self, s: str, n: int) -> str:
return s[n:]+s[:n]
或用“列表遍历拼接”和 “字符串遍历拼接”
class Solution:
def reverseLeftWords(self, s: str, n: int) -> str:
res = []
for i in range(n, len(s)):
res.append(s[i])
for i in range(0, n):
res.append(s[i])
return ''.join(res)
class Solution:
def reverseLeftWords(self, s: str, n: int) -> str:
res = ''
for i in range(n, len(s)):
res += s[i]
for i in range(0, n):
res += s[i]
return res
844. 比较含退格的字符串
双指针分别指向两个字符串的尾,存储#的个数,并跳过
注意:一次只跳过一个,且比较两个字符的时候要内嵌判断条件
class Solution:
def backspaceCompare(self, s: str, t: str) -> bool:
i, j = len(s) - 1, len(t) - 1
skipS ,skipT = 0, 0
while i >= 0 or j >= 0:
while i >= 0:
if s[i] == '#':
skipS += 1
i -= 1
else:
if skipS == 0:
break
else:
i -= 1
skipS -= 1 # 一次只跳过一个
while j >= 0:
if t[j] == '#':
skipT += 1
j -= 1
else:
if skipT == 0:
break
else:
j -= 1
skipT -= 1
if i >= 0 and j >= 0 : # 限制i >= 0 and j >= 0
if s[i] != t[j]: # 要内嵌判断条件
return False
elif i >= 0 or j >= 0: # 其中一个已经遍历完了另一个还没遍历完
return False
i -= 1
j -= 1
return True
168. Excel表列名称
class Solution:
def convertToTitle(self, columnNumber: int) -> str:
res = ''
while columnNumber:
columnNumber -= 1 # 注意每次都要-1,因为A要与1对应而不是0
n = columnNumber % 26
s = chr(n + 65) #ASCII码转成大写字符
res = s + res # 左加才是加在字符串后面
columnNumber //= 26
return res
171. Excel 表列序号
ord(columnTitle[0]) - 64得到第一位字母对应的数,后面再*26 +ord(columnTitle[i]) - 64
class Solution:
def titleToNumber(self, columnTitle: str) -> int:
cur = ord(columnTitle[0]) - 64
for i in range(1, len(columnTitle)):
cur = cur * 26 + ord(columnTitle[i]) - 64
return cur
859. 亲密字符串
分三种情况讨论:
- 两字符串长度不相等, 返回false
- 两字符串相等的时候, 有重复的元素就返回true
- 两字符串有且仅有两个不相等的地方, 判断它们交换后是否相等
class Solution:
def buddyStrings(self, s: str, goal: str) -> bool:
if len(s) != len(goal):
return False
if s == goal and len(set(s)) < len(goal):
return True
diff = []
for i in range(len(s)):
if s[i] != goal[i]:
diff.append(i)
if len(diff) == 2 and s[diff[0]] == goal[diff[1]] and s[diff[1]] == goal[diff[0]]:
return True
else:
return False
1768. 交替合并字符串
class Solution:
def mergeAlternately(self, word1: str, word2: str) -> str:
res = []
for x, y in zip_longest(word1, word2):
if x: res.append(x)
if y: res.append(y)
return ''.join(res)
451. 根据字符出现频率排序
根据字典的value进行排序
class Solution:
def frequencySort(self, s: str) -> str:
cnt = Counter(s)
res = ''
for c, v in sorted(list(cnt.items()), key = lambda x:-x[1]):
res += c * v
return res
1239. 串联字符串的最大长度
不用回溯,维护以下第i个前可用的序列组合,然后把第i个加进去,如果可用就放进去继续往后求
class Solution:
def maxLength(self, arr: List[str]) -> int:
res = 0
tmp = ['']
for s in arr:
cur = [] # 存到目前的可行解
for t in tmp:
concat = s + t
if len(set(concat)) == len(concat):
res = max(res, len(concat))
cur.append(concat)
print(cur)
tmp += cur
return res
1773. 统计匹配检索规则的物品数量
先用字典存规则的序号
class Solution:
def countMatches(self, items: List[List[str]], ruleKey: str, ruleValue: str) -> int:
rule_dict = {'type':0, 'color':1, 'name':2}
rule_no = rule_dict[ruleKey]
res = 0
for i in range(len(items)):
if items[i][rule_no] == ruleValue:
res += 1
return res
784. 字母大小写全排列
把当前字符的大小写拼在之前列表的每个字符串后面
class Solution:
def letterCasePermutation(self, s: str) -> List[str]:
res = ['']
for ch in s:
if ch.isalpha():
res = [r + ch.lower() for r in res] + [r + ch.upper() for r in res]
else:
res = [r + ch for r in res]
return res
481. 神奇字符串
找规律:s 看成是由「1 组」和「2 组」交替组成的,重点在于每组内的数字是一个还是两个,这可以从 s 自身上知道
class Solution:
def magicalString(self, n: int) -> int:
nums = [1, 2, 2]
i = 2
while i < n:
cur = 3 - nums[-1]
nums += [cur] * nums[i]
i += 1
return nums[:n].count(1)
1647. 字符频次唯一的最小删除次数
先统计字符个数,然后遍历把44332变成43210
class Solution:
def minDeletions(self, s: str) -> int:
cnt = Counter(s)
nums = sorted(list(cnt.values()), reverse = True)
res = sum(nums)
for i in range(1, len(nums)):
if nums[i] >= nums[i - 1]:
nums[i] = max(nums[i - 1] - 1, 0)
return res - sum(nums)
1668. 最大重复子字符串
遍历次数,直接判断,这个次数的word有没有在seq里面出现
class Solution:
def maxRepeating(self, sequence: str, word: str) -> int:
for i in range(len(sequence)//len(word), -1, -1):
print(word * i)
if word * i in sequence:
return i
125. 验证回文串
双指针,里面的判断部分还可以再优化一下
class Solution:
def isPalindrome(self, s: str) -> bool:
s = s.lower()
# print(s)
left, right = 0, len(s) - 1
while left <= right:
while left < len(s) and not (s[left].isalpha() or s[left].isdigit()):
left += 1
while right >= 0 and not (s[right].isalpha() or s[right].isdigit()):
right -= 1
if left == len(s) or right == -1:
return True
if s[left] != s[right]:
return False
left += 1
right -= 1
return True
1106. 解析布尔表达式
从左到右遍历表达式 expression,对于遍历到的每个字符 cc:
如果 e 是 "tf!&|" 中的一个,直接将其入栈;
如果 e 是右括号 ')',将栈中元素依次出栈,直到遇到操作符 '!' 或 '&' 或 '|'。过程中用变量 t 和 f 记录出栈字符中 't' 和 'f' 的个数。最后根据出栈字符的个数和操作符计算得到新的字符 't' 或 'f',并将其入栈。
遍历完表达式 expression 后,栈中只剩下一个字符,如果是 't',返回 true,否则返回 false。
class Solution:
def parseBoolExpr(self, expression: str) -> bool:
stk = []
for e in expression:
if e in 'tf!&|':
stk.append(e)
elif e == ')':
t = f = 0
while stk[-1] in 'tf':
t += stk[-1] == 't'
f += stk[-1] == 'f'
stk.pop()
match stk.pop():
case '!':
c = 'f' if t else 't'
case '&':
c = 'f' if f else 't'
case '|' :
c = 't' if t else 'f'
stk.append(c)
return stk[0] == 't'
1678. 设计 Goal 解析器
直接进行模拟
class Solution:
def interpret(self, command: str) -> str:
i = 0
res = ''
while i < len(command):
if command[i] == 'G':
res += 'G'
i += 1
elif command[i] == '(' and command[i + 1] == ')':
res += 'o'
i += 2
elif command[i] == '(' and command[i + 1] == 'a':
res += 'al'
i += 4
return res
1684. 统计一致字符串的数目
写法一:集合包含可以写作:set(allowed) >= set(w)
class Solution:
def countConsistentStrings(self, allowed: str, words: List[str]) -> int:
return sum([set(allowed) >= set(w) for w in words])
写法二:或者用all判断所有字符都在allowed里
class Solution:
def countConsistentStrings(self, allowed: str, words: List[str]) -> int:
allowed = set(allowed)
return sum(all(ch in allowed for ch in w) for w in words)
写法三:自己写的,不够简约
class Solution:
def countConsistentStrings(self, allowed: str, words: List[str]) -> int:
res = 0
for w in words:
for i, ch in enumerate(w):
if ch not in allowed:
break
if i == len(w) - 1:
res += 1
return res
1704. 判断字符串的两半是否相似
分别记录前后的元音字母个数
class Solution:
def halvesAreAlike(self, s: str) -> bool:
n = len(s)
dic = 'aeiouAEIOU'
cnt1, cnt2 = 0, 0
for i in range(n//2):
if s[i] in dic:
cnt1 += 1
for i in range(n//2,n):
if s[i] in dic:
cnt2 += 1
return cnt1 == cnt2
722. 删除注释
注意:while循环的i += 1写在外面,因为遍历到的字符可能在注释内,但是也要继续往下遍历
curline = []用数组比用字符效率更好
class Solution:
def removeComments(self, source: List[str]) -> List[str]:
flag = False # 不在注释内
res = []
for line in source:
i = 0
if not flag: curline = []
while i < len(line):
if line[i: i + 2] == '/*' and not flag: # 块注释开始
flag = True
i += 1
elif line[i: i + 2] == '*/' and flag: # 块注释结束
flag = False
i += 1
elif line[i: i + 2] == '//' and not flag: # 行注释开始
break
elif not flag:
curline.append(line[i])
i += 1
if curline and not flag:
res.append(''.join(curline))
return res
791. 自定义字符串排序
先把order里有的字符全部按顺序放进去,然后再把s中多余的字符放进去
class Solution:
def customSortString(self, order: str, s: str) -> str:
cnt = Counter(s)
res = ''
for ch in order:
if ch in s:
res += ch * cnt[ch]
for ch in cnt:
if ch not in res:
res += ch * cnt[ch]
return res
792. 匹配子序列的单词数
从 s 的第一个字符开始遍历,假设当前字符为 'a',从 'a' 开头的桶中取出所有单词。对于取出的每个单词,如果此时单词长度为 1,说明该单词已经匹配完毕,将答案加 1;否则将单词的首字母去掉,然后放入下一个字母开头的桶中,比如对于单词 "acd",去掉首字母 'a' 后,将其放入 'c' 开头的桶中。这一轮结束后,分桶结果变为:
c: ["cd", "ce"]
b: ["bb"]
class Solution:
def numMatchingSubseq(self, s: str, words: List[str]) -> int:
dic = defaultdict(deque)
for w in words:
dic[w[0]].append(w)
res = 0
for ch in s:
for _ in range(len(dic[ch])):
cur = dic[ch].popleft()
if len(cur) == 1:
res += 1
else:
dic[cur[1]].append(cur[1:])
return res
1758. 生成交替二进制字符串的最少操作数
解题关键:变成以1开头和变成以0开头的操作数之和为字符串的总长度n
class Solution:
def minOperations(self, s: str) -> int:
cnt = 0
n = len(s)
for i in range(n):
if int(s[i]) == i % 2:
cnt += 1
return min(cnt, n - cnt)
809. 情感丰富的文字
注意判断w的重复个数比s的多的情况
class Solution:
def expressiveWords(self, s: str, words: List[str]) -> int:
def check(s, t):
i, j = 0, 0
while i < len(s) and j < len(t):
if s[i] != t[j]:
return 0
cnt_s = 0
while i < len(s) - 1 and s[i] == s[i + 1]:
i += 1
cnt_s += 1
cnt_t = 0
while j < len(t) - 1 and t[j] == t[j + 1]:
j += 1
cnt_t += 1
if cnt_t < cnt_s < 2 or cnt_t > cnt_s:
return 0
i += 1
j += 1
if i == len(s) and j == len(t):
return 1
return 0
return sum(check(s, w) for w in words)
1796. 字符串中第二大的数字
用变量分别记录最大和第二大,依次遍历
class Solution:
def secondHighest(self, s: str) -> int:
fir, sec = -1, -1
for c in s:
if c.isdigit():
cur = int(c)
if cur > fir:
fir, sec = cur, fir
elif sec < cur < fir:
sec = cur
return sec
6253. 回环句
先对句子拆分,再比较每个单词的最后一个字母与其后一个单词的第一个字母
注意因为最后一个单词的后一个单词是第一个单词,因此使用s[(i + 1) % len(s)]来表示后一个单词
class Solution:
def isCircularSentence(self, sentence: str) -> bool:
s = sentence.split(' ')
for i, w in enumerate(s):
if w[-1] != s[(i + 1) % len(s)][0]:
return False
return True
1781. 所有子字符串美丽值之和
遍历以i开始的子字符串就只要计算一次cnt
class Solution:
def beautySum(self, s: str) -> int:
res, n = 0, len(s)
for i in range(n):
cnt = Counter()
for j in range(i, n):
cnt[s[j]] += 1
res += max(cnt.values()) - min(cnt.values())
return res
1832. 判断句子是否为全字母句
直接统计字典里元素的个数
class Solution:
def checkIfPangram(self, sentence: str) -> bool:
return len(Counter(sentence)) == 26
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示