Leetcode-字符串

刷题过程主要参考下面相关链接,所用语言为Python

注意:标题中的数字表示在LeetCode中的序号,后面的符号表示难度:E:easy,M:Medium,H:hard

每道题包括5个部分:题目,难度,方法,代码,复杂度,参考链接。

本文为字符串题目

其他类型题目:数组链表

回溯算法参考好文:https://mp.weixin.qq.com/s/tRNHG8Q6GZ_9Dfm2ljD_9w

1. 无重复字符的最长子串(3M)

3. 无重复字符的最长子串

难度:【中等】

方法:双指针 + sliding window

  • 定义两个指针 start 和 end 得到 sliding window
  • start 初始为0,用end线性遍历每个字符,用 recod 记录下每个字母最新出现的下标
  • 两种情况:一种是新字符没有在 record 中出现过,表示没有重复,一种是新字符 char 在 record 中出现过,说明 start 需要更新,取 start 和 record[char]+1 中的最大值作为新的 start。
    需要注意的是:两种情况都要对record进行更新,因为是新字符没在record出现过的时候需要添加到record中,而对于出现过的情况,也需要把record中对应的value值更新为新的下标。

参考:https://blog.csdn.net/qq_32424059/article/details/90708512

代码如下:

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        record = {}
        start,res = 0,0
        for end in range(len(s)):
            if s[end] in record:
                start = max(start, record[s[end]] + 1)
            record[s[end]] = end
            res = max(res, end - start + 1)
        return res

时间复杂度:O(n)
空间复杂度:O(1)

2. 最长回文子串(5M)

5. 最长回文子串

难度:【中等】

方法:使用中心扩展法

遍历每一个字符串,分两种情况进行中心扩展,一种为奇数,一种为偶数,如果两边字母相同,就继续扩展;如果不相同就停止扩展,对所有满足要求的长度求出最大值,得到最终答案。

参考:https://leetcode-cn.com/problems/longest-palindromic-substring/solution/zui-chang-hui-wen-zi-chuan-by-leetcode-solution/

代码如下:

class Solution:
    def expandAroundCenter(self, s, left, right):
        while left >= 0 and right < len(s) and s[left] == s[right]:
            left -= 1
            right += 1
        return left + 1, right - 1

    def longestPalindrome(self, s: str) -> str:
        start, end = 0, 0
        for i in range(len(s)):
            left1, right1 = self.expandAroundCenter(s, i, i)
            left2, right2 = self.expandAroundCenter(s, i, i + 1)
            if right1 - left1 > end - start:
                start, end = left1, right1
            if right2 - left2 > end - start:
                start, end = left2, right2
        return s[start: end + 1]

时间复杂度:O(n^2)
空间复杂度:O(1)

3. 字符串转换整数 (atoi)(8M)

https://leetcode-cn.com/problems/string-to-integer-atoi/

难度:【中等】

方法:使用正则表达式

^:匹配字符串开头
[+-]:代表一个+字符或-字符
?:前面一个字符可有可无
\d:一个数字
+:前面一个字符的一个或多个
\D:一个非数字字符
*:前面一个字符的0个或多个

参考:https://leetcode-cn.com/problems/string-to-integer-atoi/solution/python-1xing-zheng-ze-biao-da-shi-by-knifezhu/

# 使用正则表达式
class Solution:
    def myAtoi(self, s: str) -> int:
        INT_MAX = 2**31 - 1    
        INT_MIN = -2**31
        s = s.lstrip()      #清除左边多余的空格
        num_re = re.compile(r'^[\+\-]?\d+')   #设置正则规则
        num = num_re.findall(s)   #查找匹配的内容
        num = int(*num) #由于返回的是个列表,解包并且转换成整数
        return max(min(num,INT_MAX),INT_MIN)    #返回值
# 一行代码搞定,和上面的一样
class Solution:
    def myAtoi(self, s: str) -> int:
        return max(min(int(*re.findall('^[\+\-]?\d+', s.lstrip())), 2**31 - 1), -2**31)

时间复杂度:O(n)
空间复杂度:O(1)

4. 电话号码的字母组合(17M)

17. 电话号码的字母组合

难度:【中等】

方法一:回溯

  • 首先使用哈希表存储每个数字对应的多有可能的字母,然后进行回溯操作
  • 每次取电话号码的一位数字,进行回溯,直到遍历完所有的数字

参考:https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/solution/dian-hua-hao-ma-de-zi-mu-zu-he-by-leetcode-solutio/

代码如下:

class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        if not digits:
            return list()
        phoneMap = {
            '2':'abc',
            '3':'def',
            '4':'ghi',
            '5':'jkl',
            '6':'mno',
            '7':'pqrs',
            '8':'tuv',
            '9':'wxyz'
        }
        def backtrack(index):
            if index == len(digits):
                combinations.append(''.join(combination))
            else:
                digit = digits[index]
                for letter in phoneMap[digit]:
                    combination.append(letter)
                    backtrack(index + 1)
                    combination.pop()
        combination = []
        combinations = []
        backtrack(0)
        return combinations

时间复杂度: O ( 3 m × 4 n ) O\left(3^{m} \times 4^{n}\right) O(3m×4n)

空间复杂度: O ( m + n ) O(m+n) O(m+n)

5. 有效的括号(20E)

20. 有效的括号

难度:【简单】

方法:使用栈

可以先判断长度是不是偶数,如果不是偶数可以直接False

处理右括号需要看前面有没有匹配的左括号,所以可以用栈。

每次左括号就压进栈,右括号就看栈顶是不是匹配的左括号,如果不是说明有问题, 比如 [ ),就无法匹配上,如果匹配上了,就把栈顶pop掉。

最后判断是不是栈里所有左括号都脱单了。

参考:https://blog.csdn.net/qq_32424059/article/details/90748913

代码如下:

class Solution:
    def isValid(self, s: str) -> bool:
        if len(s) % 2 ==1:
            return False
        stack = []
        pairs = {']':'[',
                ')':'(',
                '}':'{'} 
        for i in s:
            if i in pairs:
                if not stack or stack[-1] != pairs[i]:
                    return False
                stack.pop()
            else:
                stack.append(i)
        return not stack

时间复杂度:O(n)
空间复杂度:O(n+∣Σ∣),Σ 为 6

6. 编辑距离(72H)

72. 编辑距离

难度:【困难】

方法:动态规划

d p [ i ] [ j ] dp[i][j] dp[i][j] 代表 word1 到 i 位置转换成 word2 到 j 位置需要最少步数

所以,

w o r d 1 [ i ] = = w o r d 2 [ j ] , d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] word1[i] == word2[j],dp[i][j] = dp[i-1][j-1] word1[i]==word2[j]dp[i][j]=dp[i1][j1]

w o r d 1 [ i ] ! = w o r d 2 [ j ] , d p [ i ] [ j ] = m i n ( d p [ i − 1 ] [ j − 1 ] , d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) + 1 word1[i] != word2[j],dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1 word1[i]!=word2[j]dp[i][j]=min(dp[i1][j1],dp[i1][j],dp[i][j1])+1

其中, d p [ i − 1 ] [ j − 1 ] dp[i-1][j-1] dp[i1][j1] 表示替换操作, d p [ i − 1 ] [ j ] dp[i-1][j] dp[i1][j] 表示删除操作, d p [ i ] [ j − 1 ] dp[i][j-1] dp[i][j1] 表示插入操作。

PS:需要特别注意的是,word的索引和dp的索引是差一个的,所以代码中的写法是

w o r d 1 [ i − 1 ] = = w o r d 2 [ j − 1 ] word1[i-1] == word2[j-1] word1[i1]==word2[j1]
在这里插入图片描述
上图中:

第一行,是 word1 为空变成 word2 最少步数,就是插入操作

第一列,是 word2 为空,需要的最少步数,就是删除操作

考点:

  • 能否理解编辑距离的含义,发现子问题,写出状态转移方程
  • 能否根据状态转移方程写出递归的解法
  • 能否意识到递归的局限性
  • 能否用进行优化

参考:
https://leetcode-cn.com/problems/edit-distance/solution/bian-ji-ju-chi-by-leetcode-solution/
https://leetcode-cn.com/problems/edit-distance/solution/zi-di-xiang-shang-he-zi-ding-xiang-xia-by-powcai-3/

代码如下:

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        m = len(word1) + 1
        n = len(word2) + 1
        dp = [[0] * n for i in range(m)]
        for i in range(n):
            dp[0][i] = i
        for j in range(m):
            dp[j][0] = j
        for i in range(1,m):
            for j in range(1,n):
                if word1[i-1] == word2[j-1]:
                    dp[i][j] = dp[i-1][j-1]
                else:
                    dp[i][j] = min(dp[i-1][j-1], dp[i][j-1], dp[i-1][j])+1
        return dp[-1][-1]

时间复杂度:O(mn)
空间复杂度:O(mn)

有效的字母异位词(242E)

242. 有效的字母异位词

难度:【简单】

三种方法:第一种是直接使用python 的 sorted函数,对字符串进行排序后,进行比较判断是都相同;第二种是使用 hashmap,将两个字符串分别统计次数放到字典中后,进行比较判断是否相同;第三种是构建一个存放26个字母的数组,统计次数,相比第二种省内存,也是最优方案。

方法一:直接使用python 的 sorted函数(也可以使用Collections中的Counter方法,直接比较)
代码如下:

from collections import Counter

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        s_ = Counter(s)
        t_ = Counter(t)
        return s_ == t_

时间复杂度:O(nlogn)
空间复杂度:O(n)

方法二:使用 hashmap

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        dic_1 = {}
        dic_2 = {}
        for i in s:
            dic_1[i] = dic_1.get(i,0) + 1
        for j in t:
            dic_2[j] = dic_2.get(j,0) + 1
        return dic_1 == dic_2

时间复杂度:O(n)
空间复杂度:O(n)

方法三:构建一个存放26个字母的数组,统计次数

class Solution(object):
    def isAnagram(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: bool
        """
        temp = [0] * 26
        for i in range(len(s)):
            temp[ord(s[i]) - ord('a')] += 1
        for j in range(len(t)):
            temp[ord(t[j]) - ord('a')] -= 1
        for k in range(26):
            if temp[k] != 0:
                return False
        else:
            return True

时间复杂度:O(n)
空间复杂度:O(1)

7. 最长公共子序列(1143M)

1143. 最长公共子序列

难度:【中等】

方法:动态规划
在这里插入图片描述

参考:https://leetcode-cn.com/problems/longest-common-subsequence/solution/zui-chang-gong-gong-zi-xu-lie-by-leetcod-y7u0/

代码如下:

class Solution:
    def longestCommonSubsequence(self, text1: str, text2: str) -> int:
        m = len(text1)
        n = len(text2)
        dp = [[0] * (n+1) for _ in range(m+1)]
        for i in range(1,m+1):
            for j in range(1,n+1):
                if text1[i-1] == text2[j-1]:
                    dp[i][j] = dp[i-1][j-1] + 1
                else:
                    dp[i][j] = max(dp[i-1][j],dp[i][j-1])
        return dp[m][n]

时间复杂度:O(mn)
空间复杂度:O(mn)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值