字符串常和其他数据类型放一起考
数组
字符串下标天然是数组
栈
后进先出,包括括号匹配、路径拆分等字符串题目,注意,python中的栈用list的append和pop实现栈的压入弹出。
哈希表
主要是用来做快速匹配
队列
先进先出
字符串方法
- 获取字符串长度: len(str)
- 判断字符串是否相等: str== str2
- 获取字符串的子串: str[begin: end:step]
- 拆分字符串: str.split(regex,maxsplit)
- 获取下标: python 只有index()
- 大小写转换: lower() upper()
- 返回指定下标的字符:str[i]
剑指 Offer II 014. 字符串中的变位词
题目描述
给定两个字符串 s1
和 s2
,写一个函数来判断 s2
是否包含 s1
的某个变位词。
换句话说,第一个字符串的排列之一是第二个字符串的 子串 。
剑指 Offer II 014. 字符串中的变位词 - 力扣(LeetCode) (leetcode-cn.com)
题解
滑动固定大小的窗口,双指针
用字符统计就可以做,遍历一次s2
def checkInclusion(self, s1: str, s2: str) -> bool: arr1, arr2, lg = [0] * 26, [0] * 26, len(s1) if lg > len(s2): return False # 维护一个arr2, 使它在s2上往右滑直到碰到尾巴 for i in range(lg): arr1[ord(s1[i]) - ord('a')] += 1 arr2[ord(s2[i]) - ord('a')] += 1 for j in range(lg, len(s2)): if arr1 == arr2: return True arr2[ord(s2[j - lg]) - ord('a')] -= 1 arr2[ord(s2[j]) - ord('a')] += 1 return arr1 == arr2
剑指 Offer II 015. 字符串中的所有变位词
题目描述
给定两个字符串 s
和 p
,找到 s
中所有 p
的 变位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
变位词 指字母相同,但排列不同的字符串
剑指 Offer II 015. 字符串中的所有变位词 - 力扣(LeetCode) (leetcode-cn.com)
题解
固定窗口双指针
上一道题改一下:
def findAnagrams(self, s: str, p: str) -> List[int]: arr1, arr2, lg = [0] * 26, [0] * 26, len(p) ret = [] if lg > len(s): return [] # 维护一个arr2, 使它在s2上往右滑直到碰到尾巴 for i in range(lg): arr1[ord(p[i]) - ord('a')] += 1 arr2[ord(s[i]) - ord('a')] += 1 for j in range(lg, len(s)+1): if arr1 == arr2: ret.append(j-lg) if j<len(s): arr2[ord(s[j - lg]) - ord('a')] -= 1 arr2[ord(s[j]) - ord('a')] += 1 return ret
剑指 Offer II 016. 不含重复字符的最长子字符串
题目描述
给定一个字符串 s
,请你找出其中不含有重复字符的 最长连续子字符串 的长度。
剑指 Offer II 016. 不含重复字符的最长子字符串 - 力扣(LeetCode) (leetcode-cn.com)
题解
双指针+hash表
双指针,维护一个字典,key为字符,value为上一次遇到该字符的位置
def lengthOfLongestSubstring(self, s: str) -> int: # 双指针+hash表 dic, res, i = {}, 0, -1 for j in range(len(s)): if s[j] in dic: i = max(dic[s[j]], i) # 更新左指针 i dic[s[j]] = j # 哈希表记录 res = max(res, j - i) # 更新结果 return res
动态规划dp
动态规划列表 dp ,dp[j] 即 代表以字符 s[j] 为结尾的 “最长不重复子字符串” 的长度。所以只需要一次for循环
def lengthOfLongestSubstring(self, s: str) -> int: #动态规划+hash表 设动态规划列表 dp ,dp[j] 即 代表以字符 s[j] 为结尾的 “最长不重复子字符串” 的长度。但是由于只要max, 只需用一个变量做动态规划额外的空间 dic = {} #记录s[i] 的 i s[i] 为距离s[j]最近的那个字符 即s[i]=s[j] i<j tmp = 0 res = 0 for j in range(len(s)): i = dic.get(s[j],-1) #获取索引i 参数-1表示找不到就返回-1 dic[s[j]] = j if tmp < j-i: #tmp中存的是dp[j-1] 即当前(shangyige)字符的dp[值] tmp = tmp+1 else: tmp = j-i res = max(res,tmp) # max(dp[j-1],dp[j]) return res
剑指 Offer II 017. 含有所有字符的最短字符串
题目描述
给定两个字符串 s 和 t 。返回 s 中包含 t 的所有字符的最短子字符串。如果 s 中不存在符合条件的子字符串,则返回空字符串 "" 。
如果 s 中存在多个符合条件的子字符串,返回任意一个。
注意: 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/M1oyTv
题解
暴力搜(当然超出时间限制,但其实还可以)
def minWindow(self, s: str, t: str) -> str: #集合 # t中所有字符都要在 lg,n = len(t),len(s) set_t = set(t) set_tmp = {} ret = n+1 res = "" for end in range(n): start = 0 while start <=end: tempset = set(s[start:end+1]) flag = True for _ in set_t: if s[start:end+1].count(_) < t.count(_): flag = False if flag and tempset.intersection(set_t) == set_t: if end-start+1<ret: ret = end-start+1 res = s[start:end+1] start+=1 return res
双指针+hash表
Counter()函数
Note: 字符串置函数count(),只能统计字符串中某个元素出现的个数。
from collections import Counter t="xbnpukocakzqzuhdlxoga" hashmap = Counter(t) print(hashmap) output: Counter({'x': 2, 'u': 2, 'k': 2, 'o': 2, 'a': 2, 'z': 2, 'b': 1, 'n': 1, 'p': 1, 'c': 1, 'q': 1, 'h': 1, 'd': 1, 'l': 1, 'g': 1})
- 设t有n个字符,则挑出来的子字符串至少应有n个字符
- 使用hashmap来保存窗口还需要的字符
def minWindow(self, s: str, t: str) -> str: # 双指针+hash表 m, n = len(s), len(t) if m < n: return "" hashmap = Counter(t) count = n #这个变量很关键,保证含有字符串t中的所有字符 ans = "" tmpLen = m + 1 i, j = 0, 0 while j < m: if s[j] in hashmap: if hashmap[s[j]] > 0: count -= 1 hashmap[s[j]] -= 1 while count == 0: if j - i + 1 < tmpLen: tmpLen = j - i + 1 ans = s[i:j+1] if s[i] in hashmap: if hashmap[s[i]] >= 0: count += 1 hashmap[s[i]] += 1 #当把左指针向右移动时,要对应更新hashmap和count i += 1 j += 1 return ans
剑指 Offer II 018. 有效的回文
题目描述
给定一个字符串 s
,验证 s
是否是 回文串 ,只考虑字母和数字字符,可以忽略字母的大小写。
本题中,将空字符串定义为有效的 回文串 。
剑指 Offer II 018. 有效的回文 - 力扣(LeetCode) (leetcode-cn.com)
题解
双指针+正则表达式
- 忽略大小写:转换成大写or小写
- 只考虑字母和数字:使用正则表达式去除多余字符
正则表达式re
Day 9:Python 字符串和正则介绍总结 - PiaYie - 博客园 (cnblogs.com)
匹配 a-z0-9
str1="".join(re.findall(r'[a-z0-9]*',s.lower()))
匹不是中文、大小写、数字的其他字符
cop = re.compile("[^\u4e00-\u9fa5^a-z^A-Z^0-9]")
def isPalindrome(self, s: str) -> bool: if not s: return true # 是否回文, # 忽略大小写 str1 = s.lower() # 只保留字母+数字 cop = re.compile("[^\u4e00-\u9fa5^a-z^A-Z^0-9]") # 匹配不是中文、大小写、数字的其他字符 str1 = cop.sub('', str1) i,j = 0,len(str1)-1 ret = True while i<=j: if str1[i]!=str1[j]: return False i+=1 j-=1 return ret
剑指 Offer II 019. 最多删除一个字符得到回文
题目描述
给定一个非空字符串 s
,请判断如果 最多 从字符串中删除一个字符能否得到一个回文字符串。
剑指 Offer II 019. 最多删除一个字符得到回文 - 力扣(LeetCode) (leetcode-cn.com)
题解
利用上题判断回文的方法
暴力搜
遍历一次数组,copy删除每一个字符后的字符串再做回文check,超出时间限制mmp
import re s ="xinoiqersxmiddtxzemwdklzwforualxmfkgvjzcqgmamrijpvmuyskclmyawsfjcwmgsvumvuklkuozziygyoyodvlhbdxgcwwvkgzgjugtscsawowvbzwutfuipozjjpwennhwxzbaswiqxrqehummyoekvfwsozusaibibfygbkxwtrdmgfhvrgwnniuvkqwneknoxomubaukvpgkjfpftxjfvytngastmzgzsxjrqcutqbnzfnhdypllfhyjntptmudayqgkfzfsitzpvxohndlzwcbyivkkazhlhfatefiaazwxishxusjztkluxkqtunamsbtkryipoaebfshocwrhukpoknqbjmojeutqwivxwmvwshvqkbxryyirpcdxufqruhodtfyobuvpvdubmahbgdudwzjpfoeyieihvfxrlatikzthubzuenjlqcnjdyloimtolntfxhgjpnbahpvaravtfvkhzvkynbmljyqnjydeljtmwmxkqxmnywmzrcrkmqgpykhjeyxybtdyktsymaojbpzyhijdlmhsubsshhacagqzcodfcyvbbfyearfmheahmprcdllgcnblwuutghqoixevurqgpvsaxueqzcsdhxoiigpjzqhjkevnzcnaerpdoqzyclkpidqghoanqaccfudqggloeclppyjidinkdydfutkivxdodorxfgzjaawngeycuhfbctmojuvtmraudilnlvqrjuxugdmwxnocwgmvcyuxegkrwxnmxubiwphjbigcpkolllcghyicsaaccfifyjjaqanqatzqbwbfgqzrmmtgrgupdsnakfolnclcyzfvwtrtyedltlsxdefpvefilecjvnrewmhodnzsgaxpeekgrtgddqowynyhsbcraroacrmcdyqhhichtyyitlfxsfuyiqmvunaraeghkuqtqkztvnrjicfbenqlffdohkcceyllircklrdeclqtdkhllci" def validPalindrome( s: str) -> bool: def isPalindrome(str1: str) -> bool: if not s: return True i,j = 0,len(str1)-1 while i<=j: if str1[i]!=str1[j]: return False i+=1 j-=1 return True str1 = s.lower() # 只保留字母+数字 cop = re.compile("[^\u4e00-\u9fa5^a-z^A-Z^0-9]") # 匹配不是中文、大小写、数字的其他字符 str1 = cop.sub('', str1) if isPalindrome(str1): return True for i in range(len(str1)): temp = str1[:i]+str1[i+1:] if isPalindrome(temp): return True return False print(validPalindrome(s))
回文性质+技巧
只要做三次check判断就可以了
def validPalindrome(self, s): # check函数用来确定两个字符是不是相等 def check(l, r): while l <= r: if s[l] != s[r]: break l += 1 r -= 1 return l, r mid = len(s) // 2 left, right = check(0, len(s) - 1) if left > mid: return True return check(left + 1, right)[0] > mid or check(left, right - 1)[0] == mid
剑指 Offer II 020. 回文子字符串的个数
题目描述
给定一个字符串 s
,请计算这个字符串中有多少个回文子字符串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
剑指 Offer II 020. 回文子字符串的个数 - 力扣(LeetCode) (leetcode-cn.com)
题解
中心扩散
def countSubstrings(self, s: str) -> int: n = len(s) #最多能向外扩散几步 def check(l,r): count = 0 while l >= 0 and r<n and s[l] == s[r]: count+=1 l -= 1 r += 1 return count ret = 0 for i in range(n): # 字串奇数长 中心为s[i] odd = check(i, i) # 字串偶数长 中心为s[i]s[i+1]这个也要比一比 even = check(i, i + 1) ret += odd ret += even return ret