420. Strong Password Checker

解法

参考这里

主要是贪心= =

  1. 长度在6~20之间,通过插入和删除操作解决
  2. 同时有大写小写和数字:通过插入、替换解决
  3. 不能连续重复大于等于3次:通过插入、删除和替换解决

贪心的思路是,优先找能同时解决多个条件的操作。

  1. 长度小于6时:
    假设缺a个字符,并且缺k种字符,且同一字符连续重复次数在3~5次。
    首先,肯定要先添加min(a,k)个字符。

    1. 如果长度够6了(min(a,k)=a),但是还差k-a种字符:

      • 如果重复次数小于等于4次,插入至少一个字符就一定能解决重复情况,所以添加min(a,k)个字符已经解决了。
      • 如果重复次数为5,那么a必然等于1,插入一个字符会分割成2+3的情况,然后再把3里面的k-a个字符替换操作就可以了。

      所以此时最小操作数为k

    2. 如果长度还没有够6(min(a,k)=k),这时候重复长度必然小于等于4,插入操作已经解决情况三,所以再随便插入a-k个字符凑够6个就可以了,此时最小操作数为a

    综上所述,长度小于6时,最小操作数为max(a,k)

  2. 长度大于等于6时:
    这时候只可能是情况一、三,情况二、三一起解决了。
    当长度大于20时,我们假设超过了b个字符,它决定了我们至少有b个删除操作。我们需要让这b个删除操作帮我们同时解决情况三。
    此外,假设我们还缺k种字符,最好的方法是通过替换同时解决情况二和三,它不会让我们的长度不达标。

    • b>0时,假设某子串连续重复了r次,我们把它减少到3m+2个,这样可以通过替换m个字符高效地解决
      如果所有的串都是3m+2长度了,那么3个3个地减少,一直到b<=0为止,这样b最小是-2,密码长度为18,还是满足条件的。

    • b<=0时,我们一定不会删除,想想看:

      1. 对于3m长度的串,只用替换需要m次,结合上删除,需要删一次成3(m-1)+2,再用m-1次替换,结果还是一样的。
      2. 对于3m+1的串,确实也需要替换m次,如果删除一个字符变成3m3m的最小操作次数是m,那么我们还多了一次删除。

      所以当b<=0时,每个长为l的字符串会需要l//3次替换

总结

总结一下整个逻辑:

  1. 当长度小于6时:次数为:max(i,k),i为缺少字符数,k为缺少字符种类数
  2. 当长度大于等于6时:
    1. 首先计算超过的字符数over
    2. 对于每个连续子串,当它的长度>=3时记录(需要删除字符数d,替换次数r)放入数组repeat
    3. 每次删除d个字符其实是为了减少一次替换,所以为了多减少替换,把d=1的优先于d=2的删除
    4. 不断删除,计算删除次数delete和替换次数replace,直到over<=delete或者len(repeat)==0
    5. 如果over>delete,说明还需要删除,此时每删除3个都能节省一次替换,我们算算最多需要减少的替换次数为:max(0,replace-k),至少要保留k次替换来满足情况二。而剩下的删除次数能减少的替换次数为:ceil((over-delete)/3),所以我们最终减少的替换次数为两者的最小值,假设为save
    6. 最终结果是删除max(over, delete)次,替换max(k, replace-save)
class Solution(object):
    def strongPasswordChecker(self, s):
        """
        :type s: str
        :rtype: int
        """
        n = len(s)
        repeat = []
        k = [1,1,1]
        beg = 0
        for i, c in enumerate(s):
            if c.isdigit():
                k[0] = 0
            elif c.isupper():
                k[1] = 0
            elif c.islower():
                k[2] = 0
            if i==0 or c!=s[i-1]:
                l = i-beg
                if l>=3:
                    d = (1 + (l % 3))%3
                    repeat.append((d, l // 3 if d != 0 else l // 3 + 1))
                beg = i
        else:
            l = n - beg
            if l >= 3:
                d = (1 + (l % 3)) % 3
                repeat.append((d, l // 3 if d!=0 else l//3+1))
        k = sum(k)
        if n<6:
            return max(6-n, k)
        repeat.sort()
        over = n-20
        delete = 0
        replace = 0
        while over>delete and len(repeat):
            d, r = repeat.pop(0)
            delete += d
            replace += (r-1)
        replace += reduce(lambda x, y: x + y[1], repeat, 0)
        if over>delete:
            save = min(max(0, replace-k),int(math.ceil((over-delete)*1.0/3)))
            delete += save*3
            replace -= save
        return max(over, delete)+max(k, replace)

猜你喜欢

转载自blog.csdn.net/lemonmillie/article/details/85636390
今日推荐