leetcode数组中的问题(五)

感觉或许写思路比较好,便于梳理总结思路,在文中会写出自己的第一思路,以及优化之后的,说人话,就是啰嗦,所以本打算设置私密的,但是发现私密不太友好,不能在自己博客上直接出来,标签归档啥的也不友好,所以设成公开,若你不小心看见了,觉得我很啰嗦,可以默默叉掉。

目录

340. 至多包含 K 个不同字符的最长子串🔒

485. 最大连续1的个数

487. 最大连续1的个数 II🔒

1004. 最大连续1的个数 III

424. 替换后的最长重复字符

992. K 个不同整数的子数组

438. 找到字符串中所有字母异位词

76. 最小覆盖子串

567. 字符串的排列

1313. 解压缩编码列表

 

340. 至多包含 K 个不同字符的最长子串

https://leetcode-cn.com/problems/longest-substring-with-at-most-k-distinct-characters/

给定一个字符串 s ,找出 至多 包含 k 个不同字符的最长子串 T。

示例 1:输入: s = "eceba", k = 2,输出: 3,解释: 则 T 为 "ece",所以长度为 3。
示例 2:输入: s = "aa", k = 1,输出: 2,解释: 则 T 为 "aa",所以长度为 2。

思路

一:159 - 至多包含两个不同字符的最长子串的解法二,只要把3改成k即可。

from collections import defaultdict
class Solution(object):
    def lengthOfLongestSubstringTwoDistinct(self, s):
        """
        :type s: str
        :rtype: int
        """
        n = len(s) 
        if n < 3:
            return n
        l, r, res, rec = 0, 0, 0, defaultdict(int)
        while r < n:
            if len(rec) < 3:
                rec[s[r]] = r
                r += 1
            if len(rec) == 3:
                del_idx = min(rec.values())
                del rec[s[del_idx]]
                l = del_idx + 1
            res = max(res, r - l)

        return res

485. 最大连续1的个数

https://leetcode-cn.com/problems/max-consecutive-ones/

给定一个二进制数组, 计算其中最大连续1的个数。

示例 1:输入: [1,1,0,1,1,1],输出: 3,解释: 开头的两位和最后的三位都是连续1,所以最大连续1的个数是 3.
注意:输入的数组只包含 0 和1。输入数组的长度是正整数,且不超过 10,000。

思路

一:一次遍历,遇到1,count加1,并与res比较确定是否需要更新;若为0,抛弃原来的计数count,令count=0。

class Solution(object):
    def findMaxConsecutiveOnes(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if not nums:
            return 0
        res, count = 0, 0
        for i in range(len(nums)):
            if nums[i] == 1:
                count += 1
                res = max(res, count)
            else:
                count = 0
        return res

二:copy一下官方解答,https://leetcode-cn.com/problems/max-consecutive-ones/solution/zui-da-lian-xu-1de-ge-shu-by-leetcode/,在 Python 中可以使用 map 和 join 来解决此问题。使用 splits 函数在 0 处分割将数组转换成字符串。在获取子串的最大长度就是最大连续 1 的长度。

class Solution(object):
    def findMaxConsecutiveOnes(self, nums):
        # print("".join(map(str,nums)).split('0'))
        return max(map(len, "".join(map(str,nums)).split('0')))
class Solution(object):
    def findMaxConsecutiveOnes(self, nums): 
        if not nums:
            return 0

        l, r, res, nearst = 0, 0, 0, -1
        while r < len(nums):
            if nums[r] == 0:
                nearst = r
                res = max(res, r - l)
            r += 1
            l = max(l, nearst + 1)
        res = max(res, r - l)
        return res

487. 最大连续1的个数 II

https://leetcode-cn.com/problems/max-consecutive-ones-ii/submissions/

给定一个二进制数组,你可以最多将 1 个 0 翻转为 1,找出其中最大连续 1 的个数。

示例 1:输入:[1,0,1,1,0],输出:4,解释:翻转第一个 0 可以得到最长的连续 1。当翻转以后,最大连续 1 的个数为 4。
注:输入数组只包含 0 和 1.输入数组的长度为正整数,且不超过 10,000
进阶:如果输入的数字是作为 无限流 逐个输入如何处理?换句话说,内存不能存储下所有从流中输入的数字。您可以有效地解决吗?

思路

一:时间复杂度O(n),rec[i]-表示以i结尾的最大连续1的个数(最多将1个0变为1)。nums[i]若是1,则直接能接到前一个元素的后面;否则,需要寻找到最近的一个零元素的下一个位置,这其中元素的个数count就是以i结尾的最大连续1的个数(最多将1个0变为1)。

class Solution(object):
    def findMaxConsecutiveOnes(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if not nums:
            return 0

        rec = [1] * len(nums)

        for i in range(1, len(nums)):
            if nums[i] != 0:
                rec[i] = rec[i-1] + 1
            else:
                count, j = 1, i-1
                while j >= 0:
                    if nums[j] == 0:
                        break
                    count += 1
                    j -= 1
                rec[i] = count
        return max(rec)

二:时间复杂度O(n),滑动窗口,r是正考虑的元素,确保[l,r)中至多只有一个0。

class Solution(object):
    def findMaxConsecutiveOnes(self, nums):
        if not nums:
            return 0

        l, r, count, res = 0, 0, 0, 0
        while r < len(nums):
            if nums[r] != 0:
                r += 1
            elif count == 0:
                count = 1
                r += 1
            else:
                while nums[l] != 0:
                    l += 1
                l += 1
                count = 0
            res = max(res, r - l)
        return res

三:改善前两种方法,因为前两种方法,均需要挪动左指针,此处记录离的最近的一个0元素的下标,无需一个个挪动左指针。

class Solution(object):
    def findMaxConsecutiveOnes(self, nums): 
        res, count, last_zero = 0, 0, -1

        for i in range(0, len(nums)):
            if nums[i] != 0:
                count += 1
            else:
                res = max(res, count)
                count = i - last_zero
                last_zero = i
        res = max(res, count)
        return res

1004. 最大连续1的个数 III

https://leetcode-cn.com/problems/max-consecutive-ones-iii/

给定一个由若干 0 和 1 组成的数组 A,我们最多可以将 K 个值从 0 变成 1 。返回仅包含 1 的最长(连续)子数组的长度。

示例 1:输入:A = [1,1,1,0,0,0,1,1,1,1,0], K = 2,输出:6,解释: [1,1,1,0,0,1,1,1,1,1,1],粗体数字从 0 翻转到 1,最长的子数组长度为 6。
示例 2:输入:A = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3,输出:10,解释:[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]粗体数字从 0 翻转到 1,最长的子数组长度为 10。
提示:1 <= A.length <= 20000;0 <= K <= A.length;A[i] 为 0 或 1 

思路

一:双指针,滑动窗口,确保[l,r)中最多只有K个0元素,这三道题均可这么理解,确保窗口中的元素符合要求。直觉k=0,可能会出错,最终通过了,但是感觉也不太逻辑,此处当K为0时会出现l>r的情况,但是好在l>r时,zero_num为-1,即下一次一定会添加元素,即下一次一定会追平,至少l<=r,所以与结果无碍,但终究不甚合乎逻辑。

class Solution(object):
    def longestOnes(self, A, K):
        """
        :type A: List[int]
        :type K: int
        :rtype: int
        """
        res, zero_num, l, r = 0, 0, 0, 0

        while r < len(A):
            if A[r] != 0:
                r += 1
            elif zero_num < K:
                r += 1
                zero_num += 1
            else:
                while l < r and A[l] != 0:
                    l += 1
                l += 1
                zero_num -= 1              
            res = max(res, r - l)
        return res

二:先添加,再剔除的方法,依旧是[l,r)中保存的符合条件的元素。

class Solution(object):
    def longestOnes(self, A, K):
        res, zero_num, l, r = 0, 0, 0, 0
 
        while r < len(A):
            if A[r] == 0:
                zero_num += 1
            r += 1
            if zero_num > K:
                while A[l] != 0:
                    l += 1
                l += 1
                zero_num -= 1
            res = max(res, r - l)
        return res
class Solution(object):
    def longestOnes(self, A, K):
        if len(A) <= K:
            return len(A)

        cnt, res = 0, 0
        l = 0

        for r in range(len(A)):
            if A[r] != 0:
                res = max(res, r - l + 1)
            elif cnt < K:
                cnt += 1
            else:
                cnt += 1
                while A[l] != 0:
                    l += 1
                l += 1
                cnt -= 1
            res = max(res, r - l + 1)
        return res

424. 替换后的最长重复字符

https://leetcode-cn.com/problems/longest-repeating-character-replacement/

给你一个仅由大写英文字母组成的字符串,你可以将任意位置上的字符替换成另外的字符,总共可最多替换 k 次。在执行上述操作后,找到包含重复字母的最长子串的长度。

注意:字符串长度 和 k 不会超过 104。

示例 1:输入:s = "ABAB", k = 2,输出:4,解释:用两个'A'替换为两个'B',反之亦然。
示例 2:输入:s = "AABABBA", k = 1,输出:4,解释:将中间的一个'A'替换为'B',字符串变为 "AABBBBA"。子串 "BBBB" 有最长重复字母, 答案为 4。

思路

一:滑动窗口,最近发现先添加,再依据条件排除,即每次右指针均向右挪动,然后判断窗口是否满足条件,满足条件更新res,不满足调整窗口大小,即挪动左指针。这边我们精确的寻找需替换的字母个数num,即窗口中所有字母个数减去频率最高的字母的个数直至小于等于k,即满足窗口条件,更新res。

from collections import defaultdict
class Solution(object):
    def characterReplacement(self, s, k):
        """
        :type s: str
        :type k: int
        :rtype: int
        """
        if len(s) <= k:
            return len(s)

        rec, l = defaultdict(int), 0
        res = 1

        for r in range(len(s)):
            rec[s[r]] += 1
            num = sum(rec.values()) - max(rec.values())
            if num <= k:
                res = max(res, r - l + 1)
            else:
                while num > k:
                    rec[s[l]] -= 1
                    l += 1
                    num = sum(rec.values()) - max(rec.values())
                res = max(res, r - l + 1)
        return res

二:参考leetcode大神题解,其实我们只需要找出替换后最长的重复字符串。这边先解释一下,max_freq:其记录的是窗口中出现的字母的最高频率(历史上),另一方面他也控制了窗口的增长,若我们新增加的字符并没有提高max_freq,则窗口长度需要维持不变,但是由于右指针右移了,故左指针也需右移,若提高了max_freq,则窗口长度可以变长,即左指针无需右移。因为每次添加一个字符,收缩最多收缩一个字符所以用的是if。其实法一中每个窗口都符合条件,但是法二中只有第一次扩大窗口长度的窗口一定是符合条件的(即下图中绿色的三个,每次更新max_freq的时候),其他的未必。好好理解哈,若太绕,就用方法一解吧,毕竟限制了只有26个字母,字典的遍历就是常数级别的,故还是O(n)时间复杂度。甩题解链接 https://leetcode-cn.com/problems/longest-repeating-character-replacement/solution/。

from collections import defaultdict
class Solution(object):
    def characterReplacement(self, s, k):
        if len(s) <= k:
            return len(s)

        rec, l = defaultdict(int), 0
        res = 1

        for r in range(len(s)):
            rec[s[r]] += 1
            num = sum(rec.values()) - max(rec.values())
            if num <= k:
                res = max(res, r - l + 1)
            else:
                rec[s[l]] -= 1
                l += 1
                res = max(res, r - l + 1)
        return res

992. K 个不同整数的子数组

https://leetcode-cn.com/problems/subarrays-with-k-different-integers/

给定一个正整数数组 A,如果 A 的某个子数组中不同整数的个数恰好为 K,则称 A 的这个连续、不一定独立的子数组为好子数组。

(例如,[1,2,3,1,2] 中有 3 个不同的整数:1,2,以及 3。)

返回 A 中好子数组的数目。

示例 1:输出:A = [1,2,1,2,3], K = 2,输入:7,解释:恰好由 2 个不同整数组成的子数组:[1,2], [2,1], [1,2], [2,3], [1,2,1], [2,1,2], [1,2,1,2].
示例 2:输入:A = [1,2,1,3,4], K = 3,输出:3,解释:恰好由 3 个不同整数组成的子数组:[1,2,1,3], [2,1,3], [1,3,4].
提示:1 <= A.length <= 20000,1 <= A[i] <= A.length,1 <= K <= A.length

思路

一:先暴力了一波,两重循环,有提前终止,O(n^2)的时间复杂度,但是没通过。

class Solution(object):
    def subarraysWithKDistinct(self, A, K):
        """
        :type A: List[int]
        :type K: int
        :rtype: int
        """
        if len(A) < K:
            return 0
        n, res = len(A) - K + 1, 0
        for i in range(n):
            rec = set()
            for j in range(i, len(A)):
                if (len(rec) < K) or (len(rec) == K and A[j] in rec):
                    rec.add(A[j])
                else:
                    break
                if len(rec) == K:
                    res += 1
        return res
                    

二:该题与通常的滑动窗口不太一样的地方在于是计数,而不是求最长最短。以[1,2,1,2,3]为例,[1,2,1]中以A[r]结尾的子数组还有[2,1],[1,2,1,2]中以A[r]结尾的子数组还有[2,1,2],[1,2],关键是如何将这些考虑进去。该方法中,每次右指针均会挪一格,若不满足条件(窗口中不同元素个数大于K,则挪动左指针直到符合要求),在符合要求的窗口中(不同元素个数为k个),再继续寻找以r结尾的所有可行解,但是不能改变左指针的位置,此处用一个新的下标来做这件事。

from collections import defaultdict
class Solution(object):
    def subarraysWithKDistinct(self, A, K):
        if len(A) < K:
            return 0
        l, r, res, rec= 0 , 0, 0, defaultdict(int)
        
        while r < len(A):
            rec[A[r]] += 1
            while len(rec) > K:
                rec[A[l]] -= 1
                if rec[A[l]] == 0:
                    del rec[A[l]]
                l += 1
            if len(rec) == K:
                tmp_l = l
                while len(rec) == K:
                    res += 1
                    rec[A[tmp_l]] -= 1
                    if rec[A[tmp_l]] == 0:
                        break
                    tmp_l += 1
                while tmp_l >= l:
                    rec[A[tmp_l]] += 1
                    tmp_l -= 1
            r += 1
        return res

438. 找到字符串中所有字母异位词

https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/submissions/

给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。

字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。

说明:字母异位词指字母相同,但排列不同的字符串。不考虑答案输出的顺序。
示例 1:输入:s: "cbaebabacd" p: "abc",输出:[0, 6],解释:起始索引等于 0 的子串是 "cba", 它是 "abc" 的字母异位词。起始索引等于 6 的子串是 "bac", 它是 "abc" 的字母异位词。
 示例 2:输入:s: "abab" p: "ab",输出:[0, 1, 2],解释:起始索引等于 0 的子串是 "ab", 它是 "ab" 的字母异位词。起始索引等于 1 的子串是 "ba", 它是 "ab" 的字母异位词。起始索引等于 2 的子串是 "ab", 它是 "ab" 的字母异位词。

思路

一:滑动窗口,此处窗口是[l,r],窗口大小固定(是字符串p的长度),因窗口大小固定,窗口长度就是p的长度,即右指针移动到某个位置后,左指针必须一同移动,且每次移动也都是一格。

from collections import defaultdict
class Solution(object):
    def findAnagrams(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: List[int]
        """
        if not s or not p or len(s) < len(p):
            return []
        l, r, n = 0, 0, len(p)
        rec_p, rec_s, result = defaultdict(int), defaultdict(int), []
        for item in p:
            rec_p[item] += 1

        # 窗口[l,r]
        while r < len(s):
            rec_s[s[r]] += 1
            r += 1               
            if r >= n:
                if self._same(rec_s, rec_p):
                    result.append(l)
                rec_s[s[l]] -= 1
                if rec_s[s[l]] == 0:
                    del rec_s[s[l]]
                l += 1 
        return result

    def _same(self, s, p):
        for k, v in s.items():
            if v != p[k]:
                return False
        return True   
from collections import Counter
class Solution(object):
    def findAnagrams(self, s, p):
        if not p or not s or len(s) < len(p):
            return []
        rec, n, l = Counter(p), len(p), 0
        window, res = {}, []

        for r in range(len(s)):
            window[s[r]] = window.get(s[r], 0) + 1
            if r - l + 1 == n:
                if self._check(window, rec):
                    res.append(l)
                window[s[l]] -= 1
                l += 1
        return res
    
    def _check(self, window, rec):
        for k, v in rec.items():
            if window.get(k) != v:
                return False
        return True

二:同样的滑动窗口,这个是参照网上一个大神的模板写出来的https://mp.weixin.qq.com/s/6YeZUCYj5ft-OGa85sQegw,以下同样转自大神文章,首先窗口是固定的,窗口长度就是输入参数中第二个字符串的长度,也就是说,右指针移动到某个位置后,左指针必须跟着一同移动,且每次移动都是一格,模版中 count 用来记录窗口内满足条件的元素,直到 count 和窗口长度相等即可更新答案。相比于方法一,该方法优化了判断是否是异位词的方法,尽管时间复杂度依旧是O(n)。

from collections import defaultdict
class Solution(object):
    def findAnagrams(self, s, p):
        # 输入参数有效性判断
        if not s or not p or len(s) < len(p):
            return []
        l, r, n = 0, 0, len(p)
        hash_table, result, count = defaultdict(int), [], 0
        # 申请一个散列,用于记录窗口中具体元素的个数情况
        for item in p:
            hash_table[item] += 1

        # 窗口[l,r]
        # l 表示左指针
        # count 记录当前的条件,具体根据题目要求来定义
        # result 用来存放结果
        while r < len(s):
            # 更新新元素在散列中的数量
            hash_table[s[r]] -= 1  
            # 根据窗口的变更结果来改变条件值 
            if hash_table[s[r]] >= 0:
                count += 1   
            # 如果当前条件不满足,移动左指针直至条件满足为止
            # 这里的条件指窗口的长度必须固定为字符串p的长度        
            if r > n - 1:
                hash_table[s[l]] += 1
                if hash_table[s[l]] > 0:
                    count -= 1
                l += 1
            r += 1  
            # 更新结果
            if count == n:
                    result.append(l)       
        return result

76. 最小覆盖子串

给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。

示例:输入: S = "ADOBECODEBANC", T = "ABC",输出: "BANC",说明:如果 S 中不存这样的子串,则返回空字符串 ""。如果 S 中存在这样的子串,我们保证它是唯一的答案。

思路

一:

from collections import Counter
class Solution(object):
    def minWindow(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: str
        """
        if not t or not s or len(s) < len(t):
            return ""
        rec, l, n = Counter(t), 0, len(t)
        local_rec = {}
        res = s + "O"

        for r in range(len(s)):
            local_rec[s[r]] = local_rec.get(s[r], 0) + 1
            if r - l + 1 >= n:
                while self._check(local_rec, rec):
                    if r - l + 1 < len(res):
                        res = s[l: r + 1]
                    local_rec[s[l]] -= 1
                    l += 1
        if len(res) > len(s):
            return ""
        return res
 
    def _check(self, local_rec, rec):
        for k, v in rec.items():
            if local_rec.get(k, 0) < v:
                return False
        return True

二:每一次保证右指针向前挪一格。若满足条件(得到可行性窗口),即窗口中包含 T 所有字母,则把左指针往前挪动若得到的窗口依然可行,则更新最小窗口大小。若窗口不再可行挪动右指针。

from collections import defaultdict
class Solution(object):
    def minWindow(self, s, t):
        if not s or not t or len(s) < len(t):
            return ""
        hash_rec = defaultdict(int)

        for c in t:
            hash_rec[c] += 1
        
        l, r, count = 0, 0, 0
        res_len, res_l, res_r = len(s) + 1, 0, 0

        while r < len(s):
            hash_rec[s[r]] -= 1

            if hash_rec[s[r]] >= 0:
                count += 1
            
            while count >= len(t):
                if count >= len(t):
                    if res_len > r - l + 1:
                        res_len = r - l + 1
                        res_l, res_r = l, r
                hash_rec[s[l]] += 1
                if hash_rec[s[l]] > 0:
                    count -= 1
                l += 1
            if count >= len(t):
                if res_len > r - l + 1:
                    res_len = r - l + 1
                    res_l, res_r = l, r
            r += 1
        if res_len > len(s):
            return ""
        return s[res_l: res_r + 1]

567. 字符串的排列

https://leetcode-cn.com/problems/permutation-in-string/

给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。换句话说,第一个字符串的排列之一是第二个字符串的子串。

示例1:输入: s1 = "ab" s2 = "eidbaooo",输出: True,解释: s2 包含 s1 的排列之一 ("ba").
示例2:输入: s1= "ab" s2 = "eidboaoo",输出: False
注意:输入的字符串只包含小写字母,两个字符串的长度都在 [1, 10,000] 之间

思路

一:其实该题与438. 找到字符串中所有字母异位词几乎完全一样,同样的代码改一下变量即可,此处题解仿的是438的方法二。

from collections import defaultdict
class Solution(object):
    def checkInclusion(self, s1, s2):
        """
        :type s1: str
        :type s2: str
        :rtype: bool
        """
        if not s1:
            return True
        if len(s2) < len(s1):
            return False
        hash_rec = defaultdict(int)
        for c in s1:
            hash_rec[c] += 1

        l, r, count = 0, 0, 0
        while r < len(s2):
            hash_rec[s2[r]] -= 1
            if hash_rec[s2[r]] >= 0:
                count += 1
            
            if r >= len(s1):
                hash_rec[s2[l]] += 1
                if hash_rec[s2[l]] > 0:
                    count -= 1
                l += 1
            if count == len(s1):
                return True
            r += 1
        return False

        

1313. 解压缩编码列表

https://leetcode-cn.com/problems/decompress-run-length-encoded-list/

给你一个以行程长度编码压缩的整数列表 nums 。考虑每对相邻的两个元素 [a, b] = [nums[2*i], nums[2*i+1]] (其中 i >= 0 ),每一对都表示解压后有 a 个值为 b 的元素。请你返回解压后的列表。

示例:输入:nums = [1,2,3,4],输出:[2,4,4,4],解释:第一对 [1,2] 代表着 2 的出现频次为 1,所以生成数组 [2]。第二对 [3,4] 代表着 4 的出现频次为 3,所以生成数组 [4,4,4]。最后将它们串联到一起 [2] + [4,4,4] = [2,4,4,4]。
提示:2 <= nums.length <= 100,nums.length % 2 == 0,1 <= nums[i] <= 100

思路

class Solution(object):
    def decompressRLElist(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        res = []
        for i in range(0, len(nums), 2):
            res.extend([nums[i + 1]] * nums[i])
        return res

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值