Leetcode 3399. Smallest Substring With Identical Characters II

1. 解题思路

这道题和题目3398除了在复杂度上面有一定的差别之外其他是完全一样的,因此我们就直接合并两道题给出同一个回答即可。

这道题的话我的一个核心思路就是二分法,首先找到答案的上下界,然后二分搜索找到临界值即可。

显然,对于下界,我们可以快速考察答案为1是否可以达到,这个我们只需要分别考察变为0开始的字符串以及1开始的字符串两种情况下所需的op数目是否有某一个小于最大允许操作数numOps即可。

而对于上界,显然一个明显的答案就是字符串的总长度n,不过这里我们稍微做了一下简化,可以通过二分的方式简化一下答案,即不断取最大长度字串进行二分,此时我们可以简化得到一个必然可以获得的最小长度(但这里我们无法获得2以下的长度答案)。

然后,在这个区间范围内我们进行考察其是否可以在最大允许操作数numOps下进行分割即可。而这个我们可以通过动态规划进行实现。

2. 代码实现

给出python代码实现如下:

class Solution:
    def minLength(self, s: str, numOps: int) -> int:
        n = len(s)
        
        def is_possible_at1():
            cnt0, cnt1 = 0, 0
            for i, ch in enumerate(s):
                if i % 2 == 0:
                    if ch == '0':
                        cnt1 += 1
                    else:
                        cnt0 += 1
                else:
                    if ch == '1':
                        cnt1 += 1
                    else:
                        cnt0 += 1
            return (cnt0 <= numOps or cnt1 <= numOps)
        
        if is_possible_at1():
            return 1
        
        def flip(ch):
            return "1" if ch == "0" else "0"
                
        @lru_cache(None)
        def is_possible(idx, digit, max_len, ops):
            if idx >= n:
                return True
            if s[idx] != digit:
                if ops == 0:
                    return False
                else:
                    ops -= 1
            if ops == 0:
                pre, cnt = digit, 1
                for j in range(idx+1, n):
                    if s[j] == pre:
                        cnt += 1
                    else:
                        cnt = 1
                    if cnt > max_len:
                        return False
                    pre = s[j]
                return True
            i = idx+1
            while i < min(n, idx+max_len):
                if s[i] != digit:
                    return is_possible(i, s[i], max_len, ops)
                i += 1
            return is_possible(i, flip(digit), max_len, ops)
                    
        def get_binary_split_answer():
            q = []
            pre, cnt = s[0], 0
            for ch in s:
                if ch == pre:
                    cnt += 1
                else:
                    if cnt > 2:
                        q.append(-cnt)
                    cnt = 1
                pre = ch
            if cnt > 2:
                q.append(-cnt)
            heapq.heapify(q)
            for _ in range(numOps):
                if len(q) == 0:
                    break
                m = - heapq.heappop(q)
                l, r = m // 2, m-1 - (m//2)
                if l > 2:
                    heapq.heappush(q, -l)
                if r > 2:
                    heapq.heappush(q, -r)
            return -q[0] if len(q) > 0 else 2
            
        l = 1
        r = get_binary_split_answer()
        while r-l > 1:
            m = (l+r) // 2
            if is_possible(0, s[0], m, numOps):
                r = m
            else:
                l = m
        return r

提交代码评测得到:耗时1101ms,占用内存80MB。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值