Leetcode题解-数据结构-字符串(python版)

1、两字符的组成字符是否相等

242. 有效的字母异位词(Easy)
方法一:排序

class Solution(object):
    def isAnagram(self, s, t):
        return sorted(s) == sorted(t)

方法二:哈希表
统计 s 和 t 中每一个字符出现的次数

class Solution(object):
    def isAnagram(self, s, t):
        return collections.Counter(s) == collections.Counter(t)

2、一组字符可以组成回文字符串最大长度

409. 最长回文串(Easy)

class Solution(object):
    def longestPalindrome(self, s):
        letter_num = collections.Counter(s)
        flag = 0
        res = 0
        for value in letter_num.values():
            if value%2:
                flag = 1
                res += value-1
            else:
                res += value
        return res + flag

3、字符串同构

205. 同构字符串(Medium)

class Solution(object):
    def isIsomorphic(self, s, t):
        relation = {}
        for i in range(len(s)):
            if s[i] in relation:
                if relation[s[i]] != t[i]:
                    return False
            else:
                if t[i] in relation.values():
                    return False
                relation[s[i]] = t[i]
        return True

4、回文子字符串的个数

647. 回文子串(Easy)

中心拓展
如果回文字符串的长度为偶数,就从中间两个字符逐渐向外走,逐一匹配。
如果回文字符串的长度为奇数,就从中间一个字符逐渐向外走,逐一匹配。

class Solution(object):
    res = 0
    def countSubstrings(self, s):
        n = len(s)
        def maxlen(left, right):
            while 0 <= left <= right < n and s[left] == s[right]:
                left -= 1
                right += 1
                self.res += 1
            return right-left-1
        for i in range(n):
            maxlen(i, i)
            maxlen(i, i+1)
        return self.res

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

5、判断一个数是不是回文数

9. 回文数(Easy)

方法一:反转数字后半部分

class Solution(object):
    def isPalindrome(self, x):
        if x < 0 or (x%10 ==0 and x != 0):
            return False
        right = 0
        while x > right:
            right = right * 10 + x%10
            x = x//10
        return right == x or x == right//10
        """
        :type x: int
        :rtype: bool
        """

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

方法二:将数字转换为字符串,并检查字符串是否为回文。

class Solution(object):
    def isPalindrome(self, x):
        return str(x) == str(x)[::-1]

6、统计二进制字符串中连续 1 和连续 0 数量相同的子字符串个数

696. 计数二进制子串(Easy)

将字符串 s 按照 0 和 1 的连续段分组,存在数组中,例如 s = 00111011,可以得到这样的 {2,3,1,2}。数组中两个相邻的数一定代表的是两种不同的字符。假设数组中两个相邻的数字为 u 或者 v,它们能组成的满足条件的子串数目为 min{u,v}。将相邻的数对较小值相加即可得到答案。

可以不用数组,我们只需要知道当前连续的数字出现的次数和上次连续数字出现的次数,取最小值即可。

class Solution(object):
    def countBinarySubstrings(self, s):
        pre,cur = 0, 1
        res = 0
        for i in range(1, len(s)):
            if s[i] == s[i-1]:
                cur += 1
            else:
                res += min(pre, cur)
                pre = cur
                cur = 1
        return res + min(pre, cur)

7、旋转字符串

796. 旋转字符串(Easy)
方法一:穷举,将字符串一次移位,看是否和目标字符串相等

class Solution(object):
    def rotateString(self, A, B):
        if len(A) != len(B):
            return False
        if not A:
            return True
        n = len(A)
        for i in range(n):
            newA = A[i:] + A[:i]
            if newA == B:
                return True
        return False

方法二:
判断 B 是否为 A + A 的子串

class Solution(object):
    def rotateString(self, A, B):
        return len(A) == len(B) and B in A + A

8、替换所有的问号

1576. 替换所有的问号(Easy)

本题的思路就是对字符串中每个字符进行遍历,对于问好,将其换为与其左右字符否不相同的字母即可,其实只需要 abc 三个字母就可以完成替换,且相邻字母不重复,为了处理方便,我们将原字符串首位加上字符 “?”,这样处理的时候就不用关心首位字符的问题了。

class Solution(object):
    def modifyString(self, s):
        chs = ["a", "b", "c"]
        s = "?" + s + "?"
        n = len(s)
        for i in range(1, n-1):
            if s[i] == "?":
                for ch in chs:
                    if ch != s[i-1] and ch != s[i+1]:
                        s = s[:i] + ch + s[i+1:]
        return s[1:-1]

9、重复的子字符串

459. 重复的子字符串(Easy)

方法一:枚举

如果存在重复的子字符串,重复的子字符串一定从第一个字符开始,我们从长度 1 开始,枚举每一种可能出现的重复子串。

如果一个长度为 n 的字符串 s 可以由它的一个长度为 n’ 的子串 s’ 重复多次构成,那么:

  • n 一定是 n’ 的倍数
  • s′ 一定是 s 的前缀
  • 对于任意的 i∈[n′ ,n),有 s[i] = s[i-n’]

先枚举一种可能的重复子字符串,然后验证这个子字符串是不是真的重复子字符串。

class Solution(object):
    def repeatedSubstringPattern(self, s):
        n = len(s)

        def is_repeated(s, repeat_len):
            n = len(s)
            if n % repeat_len != 0:
                return False
            times = n // repeat_len
            for time in range(times-1):
                if s[time * repeat_len:(time+1)*repeat_len] != s[(time+1)*repeat_len:(time+2)*repeat_len]:
                    return False
            return True

        for i in range(1, n//2+1):
            if s[:i] == s[i:2*i]:
                if is_repeated(s, i):
                    return True
        return False

该种方法也有这种写法,就是再验证,n 一定是 n’ 的倍数,对于任意的 i∈[n′ ,n),有 s[i] = s[i-n’]

class Solution(object):
    def repeatedSubstringPattern(self, s):
        n = len(s)
        for i in range(1, n//2+1):
            if n%i == 0:
                if all(s[j] == s[j-i] for j in range(i, n)):
                    return True
        return False

方法二:字符串匹配

我们可以把字符串 s 写成 s’s’s’s’s′s′ 的形式,总计 n/n’ 个 s’。将第一个 s’ 保持顺序添加到剩余字符串的末尾,那么得到的字符串仍然是 s。

注意到我们证明的是如果 s 满足题目要求,那么 s 有这样的性质,而我们使用的方法却是如果 s 有这样的性质,那么 s 满足题目要求。因此,只证明了充分性是远远不够的,我们还需要证明必要性。证明比较麻烦,这里就不给出证明了。

class Solution(object):
    def repeatedSubstringPattern(self, s):
        return (s+s).find(s, 1) != len(s)

10、破坏回文串

1328. 破坏回文串(Easy)

回文串前半段和后半段是相互对应的,因此只要遍历一半就好了。

  • 首先遍历前半段,遇到不为 a 的字符就直接将其替换成 a,然后直接 return 结果。
  • 如果前半段都是a,则说明后半段也都是a,说明字符串要么类似aabaa,要么类似aaaaaa。直接将最后1个字符改成b就好了。
class Solution:
    def breakPalindrome(self, palindrome: str) -> str:
        n = len(palindrome)
        if n <= 1:
            return ""
        for i in range(n//2):
            if palindrome[i] != 'a':
                return palindrome[:i] + 'a' + palindrome[i+1:]
        return palindrome[:-1] + "b"

11、移除无效的括号

1249. 移除无效的括号(Medium)
使用栈,用 num 记录左括号比 “(” 右括号 “)” 多的数量,遍历字符串,如果当右括号数量大于左括号数量时,当前右括号不入栈,否则元素都要入栈,并记录左括号比右括号多的数量 num。然后元素依次出栈,将前 num 个左括号舍弃,这样得到的字符串括号相匹配。

class Solution:
    def minRemoveToMakeValid(self, s: str) -> str:
        item = []
        num = 0
        for c in s:
            if c == "(":
                num += 1
            if c == ")":
                if num <= 0:
                    continue
                else:
                    num -= 1
            item.append(c)
        res = ""
        while item:
            if num > 0 and item[-1] == "(":
                num -= 1
                item.pop()
                continue
            res = item.pop() + res
        return res

12、生成每种字符都是奇数个的字符串

1374. 生成每种字符都是奇数个的字符串(Easy)

n 为奇数,直接返回奇数个 a,n 为偶数,返回 n-1 个 a 和一个 b

class Solution:
    def generateTheString(self, n: int) -> str:
        return 'a'*(n-1) + "b" if n%2 == 0 else 'a' * n

13、亲密字符串

859. 亲密字符串(Easy)

如果连个字符串相等,只要有一个字符出现个数大于 2 即可,如果两字符不相等,将不相等的元素取出,如果正好是两个元素,且两元素交换后相等就是亲密字符串。

class Solution:
    def buddyStrings(self, A: str, B: str) -> bool:
        if len(A) != len(B):
            return False
        if A == B:
            dict = collections.Counter(A)
            for v in list(dict.values()):
                if v > 1:
                    return True

        n = len(A)
        diffA, diffB = "", ""
        for i in range(n):
            if A[i] != B[i]:
                diffA += A[i]
                diffB += B[i]
        return len(diffA) == 2 and diffA == diffB[::-1]

14、仅仅反转字母

917. 仅仅反转字母(Easy)
将所有的字母放入栈中,遍历元字符串,若是字母,则从栈中取出字母,若是其他字符,直接拿过来。

class Solution:
    def reverseOnlyLetters(self, S: str) -> str:
        letter = [c for c in S if c.isalpha()]
        res = ""
        for c in S:
            if c.isalpha():
                res += letter.pop()
            else:
                res += c
        return res

15、模糊坐标

816. 模糊坐标(Medium)

字符串分割成两块,分别对每一块进行判断,一共有四种情况

  1. 首尾均是0,违规;
  2. 首0,只有一种情况,即0.XXX
  3. 尾0,只有一种情况,即XXXX0
  4. 其他的每个间隔位均可插入0
class Solution:
    def ambiguousCoordinates(self, S: str) -> List[str]:
        def getrightnum(s):
            if s == '0':
                return [s]
            if s.startswith("0"):
                if s.endswith("0"):
                    return []
                return ["0." + s[1:]]
            else:
                if s.endswith("0"):
                    return [s]
                res = [s]
                for i in range(1, len(s)):
                    res.append(s[:i] + "." + s[i:])
                return res
        
        S = S[1:-1]
        res = []
        for i in range(1, len(S)):
            left = getrightnum(S[:i])
            right = getrightnum(S[i:])
            for l in left:
                for r in right:
                    res.append("(%s, %s)" % (l, r))
        return res

16、仅含 1 的子串数

1513. 仅含 1 的子串数(Medium)

计算每一个连续数字 1 的长度,长度为 n 的全 1 串包含子串数量为 1+2+3+……+n = (1+n)*n/2

具体做法是,从左到右遍历字符串,如果遇到字符 1 则计算连续字符 1 的数量,如果遇到字符 0 则说明上一个只包含字符 1 的最长子字符串遍历结束,计算这个只包含字符 1 的子串数量,累加起来。遍历结束后,如果连续字符 1 的数量大于零,则还有最后一个只包含字符 1 的最长子字符串,别忘了最会一个只包含 1 的子串。

class Solution:
    def numSub(self, s: str) -> int:
        cur_num = 0
        res = 0
        for c in s:
            if c == '1':
                cur_num += 1
            else:
                res += (cur_num + 1)*cur_num//2
                cur_num = 0
        res += (cur_num + 1)*cur_num//2
        return res%(10**9+7)

17、最优除法

553. 最优除法(Medium)

被除数最大,除数最小即可:

  • 当nums.size()<=2 的时候不需要添加括号
  • 当nums.size()>2 时,只要满足被除数最大,除数最小即可,用一个括号即可完成!
class Solution:
    def optimalDivision(self, nums: List[int]) -> str:
        n = len(nums)
        if n == 1:
            return str(nums[0])
        res = str(nums[0]) + "/("
        if n == 2:
            return str(nums[0]) + '/' + str(nums[1])
        for i in range(1, n):
            res += str(nums[i]) + "/"
        return res[:-1] + ")"

18、验证IP地址是ipv4还是ipv6

468. 验证IP地址(Medium)

对于 IPv4 地址,通过界定符 ‘.’ 将地址分为四块;对于 IPv6 地址,通过界定符 ‘:’ 将地址分为八块。

  • 对于 IPv4 地址的每一块,检查它们是否在 0 - 255 内,且没有前置零。
  • 对于 IPv6 地址的每一块,检查其长度是否为 1 - 4 位的十六进制数。
class Solution:
    def validate_ip4(self, IP):
        nums =IP.split('.')
        for x in nums:
            if len(x) == 0 or len(x) > 3:
                return "Neither"
            if x[0] == '0' and len(x) > 1 or not x.isdigit() or int(x) > 255:
                return "Neither"
        return "IPv4"

    def validate_ip6(self, IP):
        nums = IP.split(':')
        hexdight = "0123456789abcdefABCDEF"
        for x in nums:
            if len(x) == 0 or len(x) > 4 or not all (c in hexdight for c in x):
                return "Neither"
        return "IPv6"
    def validIPAddress(self, IP: str) -> str:
        if IP.count('.') == 3:
            return self.validate_ip4(IP)
        if IP.count(':') == 7:
            return self.validate_ip6(IP)
        else:
            return "Neither"

19、两个相同字符之间的最长子字符串

1624. 两个相同字符之间的最长子字符串(Easy)

统计每种字符在字符串中的位置,再计算第一个位置和最后一个位置之间距离

class Solution:
    def maxLengthBetweenEqualCharacters(self, s: str) -> int:
        char_dict = collections.defaultdict(list)
        for i, c in enumerate(s):
            char_dict[c].append(i)
        max_len = -1
        for _, c_list in char_dict.items():
            if len(c_list) < 2:
                continue
            max_len = max(max_len, c_list[-1] - c_list[0] -1)
        return max_len

也可以不统计一个字符所有出现的位置,只记录第一次出现的位置,下次出现的时候,更新最长的距离就可以了

class Solution:
    def maxLengthBetweenEqualCharacters(self, s: str) -> int:
        d = {}
        res = -1
        for idx, ch in enumerate(s):
            if ch in d:
                res = max(res, idx-d[ch]-1)
            else:
                d[ch] = idx				# 第一次出现,位置记录下来
        return res

20、改变一个整数能得到的最大差值

1432. 改变一个整数能得到的最大差值(Medium)

对于最大值,我们尽量把高位不是 9 的替换成 9 就会让值尽可能地大;
对于最小值,如果是第一位,尽量替换成 1;如果不是第一位,就尽量替换成 0(这里需要和第一位作比较,不能替换后有前导0)。

class Solution:
    def maxDiff(self, num: int) -> int:
        if num < 10:
            return 8
        num = str(num)
        max_num, min_num = num, num

        for i, c in enumerate(num):
            if c != '9':
                max_num = num.replace(c, '9')
                break
        for i, c in enumerate(num):
            if i == 0 and c != '1':
                min_num = num.replace(c, '1')
                break
            elif i > 0 and c != '0' and c != num[0]:
                min_num = num.replace(c, '0')
                break
        return int(max_num) - int(min_num)

21、比较字符串最小字母出现频次

1170. 比较字符串最小字母出现频次(Easy)

统计两个字符串数组queries、words中每个串的 f(s),分别记录 在 num_queries、words_queries 中,依次查找 words_queries 中每个数字大于 num_queries 中数字的个数,添加到列表

class Solution:
    def min_char_num(self, s):
        s = sorted(s)
        return s.count(s[0])
    def numSmallerByFrequency(self, queries: List[str], words: List[str]) -> List[int]:
        num_queries, words_queries = [], []
        for s in queries:
            num_queries.append(self.min_char_num(s))
        for s in words:
            words_queries.append(self.min_char_num(s))

        res = []
        for i in num_queries:
            tmp = 0
            for j in words_queries:
                if i < j:
                    tmp += 1
            res.append(tmp)
        return res
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值