日常系列LeetCode《4·字符串篇》
Posted 常某某的好奇心
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了日常系列LeetCode《4·字符串篇》相关的知识,希望对你有一定的参考价值。
数据规模->时间复杂度
<=10^4 😮(n^2)
<=10^7:o(nlogn)
<=10^8:o(n)
10^8<=:o(1)
总结:
字符串匹配相关
字符串反转相关
字符串数字转换相关
暴力解法、RK算法、BM 算法、KMP算法
lc 28 :https://leetcode.cn/problems/implement-strstr/
提示:
1 <= haystack.length, needle.length <= 10^4
haystack 和 needle 仅由小写英文字符组成
#方案一:暴力解法
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
m,n=len(haystack),len(needle)
#o(1)
if n==0:return 0
if n>m:return -1
#o(m*n)
for i in range(m-n+1):
if haystack[i]==needle[0] and haystack[i:i+n]==needle:
return i
#
return -1
#方案二:KMP(动态规划解法)
#KMP的做法是:根据好前缀字符串的特点,来计算出模式串往后移的位数
#关键:找到好前缀的【最长匹配前缀子串】
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
m,n=len(haystack),len(needle)
#
if n==0:return 0
if n>m:return -1
#o(m+n)?
nexts=self.getnexts(needle)
j=0
for i in range(m):
#过渡:好前缀的下一位(i,j)
while j>0 and haystack[i] != needle[j]:
j=nexts[j-1]+1
#比较
if haystack[i]==needle[j]:j+=1
if j==n:return i-n+1
#
return -1
def getnexts(self,needle:str)-> List[int]:
#
n=len(needle)
if n==1:return []
#o(n)
nexts=[-1]*(n-1) #n-1:一个坏字符
#o(n)
for i in range(1,n-1):
pre=nexts[i-1]
#过渡:若前最长子串的下一个字符不与最后一个i相等,找前一个的次长串
while pre!=-1 and needle[pre+1] != needle[i]:
pre=nexts[pre]
#计算
if needle[pre+1] == needle[i]:
nexts[i]=pre+1
else:nexts[i]=pre
#
return nexts
lc 459 :https://leetcode.cn/problems/repeated-substring-pattern/
提示:
1 <= s.length <= 10^4
s 由小写英文字母组成
#方案一:双指针
class Solution:
def repeatedSubstringPattern(self, s: str) -> bool:
#o(1)
n=len(s)
#o(n^2)
for ln in range(1,(n//2)+1):
#
if n%ln==0:
i=0
j=ln
while j<n:
if s[i]!=s[j]:
break
else:
j+=1
i+=1
#
if j==n:return True
#
return False
#方案二:旋转数组思路
#方案三:字符串拼接找匹配
class Solution:
def repeatedSubstringPattern(self, s: str) -> bool:
#o(n),o(n^2)[KMP O(n+n)]
return (s+s).find(s,1)!=len(s) #若原字符串头尾对称,则拼接后,尾头=原字符串
lc 344 :https://leetcode.cn/problems/reverse-string/
提示:
1 <= s.length <= 10^5
s[i] 都是 ASCII 码表中的可打印字符
【1·篇】
lc 345 :https://leetcode.cn/problems/reverse-vowels-of-a-string/
提示:
1 <= s.length <= 3 * 10^5
s 由 可打印的 ASCII 字符组成
class Solution:
def reverseVowels(self, s: str) -> str:
#o(n)
s=list(s)#注意
left,right=0,len(s)-1
#o(n)
while left<right:
#直到元音
while left<right and not self.isvowel(s[left]):left+=1
while left<right and not self.isvowel(s[right]):right-=1
#交换
s[left],s[right]=s[right],s[left]
left+=1
right-=1
#
return ''.join(s)#注意
def isvowel(self,c:str) -> bool:
return c == 'a' or c == 'e' or c == 'i' or c == 'o' or c == 'u' or c == 'A' or c == 'E' or c == 'I' or c == 'O' or c == 'U'
lc 1119 :https://leetcode.cn/problems/remove-vowels-from-a-string/
提示:
1 <= S.length <= 1000
s 仅由小写英文字母组成
class Solution:
def removeVowels(self, s: str) -> str:
#o(n)
s=list(s)
res=""
#o(n)
for c in s:
if not self.isvowel(c):
res+=c
#
return res
def isvowel(self,c:str) -> bool:
return c == 'a' or c == 'e' or c == 'i' or c == 'o' or c == 'u' or c == 'A' or c == 'E' or c == 'I' or c == 'O' or c == 'U'
lc 541 :https://leetcode.cn/problems/reverse-string-ii/
提示:
1 <= s.length <= 10^4
s 仅由小写英文组成
1 <= k <= 10^4
class Solution:
def reverseStr(self, s: str, k: int) -> str:
#o(k)
s=list(s)
#o(k^2)
for start in range(0,len(s),2*k):
#定位
left=start
right=min(left+k-1,len(s)-1)
#交换
while left<right:
s[left],s[right]=s[right],s[left]
left+=1 #注意遗忘
right-=1
#
return "".join(s)
lc 557 :https://leetcode.cn/problems/reverse-words-in-a-string-iii/
提示:
1 <= s.length <= 5 * 10^4
s 包含可打印的 ASCII 字符。
s 不包含任何开头或结尾空格。
s 里 至少 有一个词。
s 中的所有单词都用一个空格隔开。
class Solution:
def reverseWords(self, s: str) -> str:
#o(n)
s=list(s)
#o(n^2)
left=0
while left<len(s):
#定位
if s[left]!=' ':
right=left
while right+1<len(s) and s[right+1]!=' ':right+=1
#反转
i,j=left,right#注意点:直接用left,right会篡改
while i<j:
s[i],s[j]=s[j],s[i]
i+=1
j-=1
#更新
left=right+2
else:left+=1
#
return ''.join(s)
lc 58 :https://leetcode.cn/problems/length-of-last-word/
提示:
1 <= s.length <= 10^4
s 仅有英文字母和空格 ’ ’ 组成
s 中至少存在一个单词
#从右向左遍历
class Solution:
def lengthOfLastWord(self, s: str) -> int:
#o(1)
end=len(s)-1
#o(n)
while end>=0 and s[end]==' ':end-=1
start=end
if end<0:return 0
while start>=0 and s[start]!=' ':start-=1
return end-start
lc 8【剑指 67】 :https://leetcode.cn/problems/string-to-integer-atoi/
提示:
0 <= s.length <= 200
s 由英文字母(大写和小写)、数字(0-9)、’ ‘、’+‘、’-’ 和 ‘.’ 组成
class Solution:
def myAtoi(self, s: str) -> int:
#o(n)
s=list(s)
#o(n)
#'去空'
i=0
while i<len(s) and s[i]==' ':i+=1
if i==len(s):return 0
#判号
sign=1
if s[i]=='-' or s[i]=='+':
if s[i]=='-':sign=-1
i+=1
#检查溢出与计算
base=0
while i<len(s) and s[i]>='0' and s[i]<='9':
#溢出问题
if base > (2**31-1)//10 or (base==(2**31-1)//10 and ord(s[i])-ord('0')>7):
if sign==1:return 2**31-1
else:return -2**31
#计算
base=base*10+(ord(s[i])-ord('0'))
#更新
i+=1 #注意遗忘
#
return sign*base
lc 165 :https://leetcode.cn/problems/compare-version-numbers/
提示:
1 <= version1.length, version2.length <= 500
version1 和 version2 仅包含数字和 ‘.’
version1 和 version2 都是 有效版本号
version1 和 version2 的所有修订号都可以存储在 32 位整数 中
#方案一:内置函数
#方案二:不使用内函
class Solution:
def compareVersion(self, version1: str, version2: str) -> int:
#o(n)
n1,n2=len(version1),len(version2)
s1,s2=list(version1),list(version2)
#o(n^2)
i=j=0
while i<n1 or j<n2:
v1=v2=0#注:位置(对应编号的对比)
while i<n1 and s1[i]!='.':
v1=v1*10+(ord(s1[i])-ord('0'))
i+=1#注:位置
while j<n2 and s2[j]!='.':
v2=v2*10+(ord(s2[j])-ord('0'))
j+=1#注:位置
if v1!=v2:
return 1 if v1>v2 else -1
#更新
i,j=i+1,j+1#注意遗忘
#
return 0
lc 12:https://leetcode.cn/problems/integer-to-roman/
提示:
1 <= num <= 3999
class Solution:
def intToRoman(self, num: int) -> str:
#o(n)
nums=[1,4,5,9,10,40,50,90,100,400,500,900,1000]
romans=['I','IV','V','IX','X','XL','L','XC','C','CD','D','CM','M']
res=''
#o(n^2)
i=12
while 0<=i<13:
#规则:799=500+100+100(if->while)+90+9
while nums[i]<=num:
res+=romans[i]
num-=nums[i]
#注意更新
i-=1
#
return ''.join(res)
lc 13 :https://leetcode.cn/problems/roman-to-integer/
提示:
1 <= s.length <= 15
s 仅含字符 (‘I’, ‘V’, ‘X’, ‘L’, ‘C’, ‘D’, ‘M’)
题目数据保证 s 是一个有效的罗马数字,且表示整数在范围 [1, 3999] 内
题目所给测试用例皆符合罗马数字书写规则,不会出现跨位等情况。
IL 和 IM 这样的例子并不符合题目要求,49 应该写作 XLIX,999 应该写作 CMXCIX 。
关于罗马数字的详尽书写规则,可以参考 罗马数字 - Mathematics
#通常情况下,罗马数字中小的数字在大的数字的右边。小的数字在大的数字前面的六种情况
class Solution:
def romanToInt(self, s: str) -> int:
#o(n):字典
values = 'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000
#o(n)
num=0
pre=values.get(s[0],0)#默认值0
for i in range(1,len(s)):
curr=values.get(s[i],0)
#规则:通常小在后
if pre<curr:
num-=pre
else:num+=pre
#值更新
pre=curr
#
num+=pre
#
return num
lc 38 :https://leetcode.cn/problems/count-and-say/
提示:
1 <= n <= 30
class Solution:
def countAndSay(self, n: int) -> str:
#o(n):初始
curr='1'
#o(n^2):更新
for i in range(1,n):
#每次初始
pre,curr=curr,'' #每次更新:curr->pre,curr初始化
count,say=1,pre[0]
#每次更新:count、pre
for j in range(1,len(pre)):
if pre[j]==say:
count+=1
else:
curr+=str(count)
curr+=str(say)
count,say=1,pre[j]
#
curr +=str(count)
curr +=str(say)
#
return curr
lc 6 :https://leetcode.cn/problems/zigzag-conversion/
提示:
1 <= s.length <= 1000
s 由英文字母(小写和大写)、‘,’ 和 ‘.’ 组成
1 <= numRows <= 1000
class Solution:
def convert(self, s: str, numRows: int) -> str:
#
if numRows==1: return s
#o(n)
res=以上是关于日常系列LeetCode《4·字符串篇》的主要内容,如果未能解决你的问题,请参考以下文章