日常系列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·字符串篇》的主要内容,如果未能解决你的问题,请参考以下文章

日常系列LeetCode《2·一维数组篇》

日常系列LeetCode《9·哈希查找篇》

日常系列LeetCode《10·栈和队列篇》

日常系列LeetCode《12·滑动窗口篇》

日常系列LeetCode《13·综合应用1篇》

日常系列LeetCode《6·位运算篇》