解法
参考这里
主要是贪心= =
- 长度在6~20之间,通过插入和删除操作解决
- 同时有大写小写和数字:通过插入、替换解决
- 不能连续重复大于等于3次:通过插入、删除和替换解决
贪心的思路是,优先找能同时解决多个条件的操作。
-
长度小于6时:
假设缺a个字符,并且缺k种字符,且同一字符连续重复次数在3~5次。
首先,肯定要先添加min(a,k)
个字符。-
如果长度够6了(
min(a,k)=a
),但是还差k-a
种字符:- 如果重复次数小于等于4次,插入至少一个字符就一定能解决重复情况,所以添加
min(a,k)
个字符已经解决了。 - 如果重复次数为5,那么a必然等于1,插入一个字符会分割成2+3的情况,然后再把3里面的
k-a
个字符替换操作就可以了。
所以此时最小操作数为k
- 如果重复次数小于等于4次,插入至少一个字符就一定能解决重复情况,所以添加
-
如果长度还没有够6(
min(a,k)=k
),这时候重复长度必然小于等于4,插入操作已经解决情况三,所以再随便插入a-k个字符凑够6个就可以了,此时最小操作数为a
综上所述,长度小于6时,最小操作数为
max(a,k)
-
-
长度大于等于6时:
这时候只可能是情况一、三,情况二、三一起解决了。
当长度大于20时,我们假设超过了b个字符,它决定了我们至少有b个删除操作。我们需要让这b个删除操作帮我们同时解决情况三。
此外,假设我们还缺k种字符,最好的方法是通过替换同时解决情况二和三,它不会让我们的长度不达标。-
当
b>0
时,假设某子串连续重复了r次,我们把它减少到3m+2
个,这样可以通过替换m个字符高效地解决
如果所有的串都是3m+2
长度了,那么3个3个地减少,一直到b<=0
为止,这样b最小是-2,密码长度为18,还是满足条件的。 -
当
b<=0
时,我们一定不会删除,想想看:- 对于
3m
长度的串,只用替换需要m次,结合上删除,需要删一次成3(m-1)+2
,再用m-1次替换,结果还是一样的。 - 对于
3m+1
的串,确实也需要替换m
次,如果删除一个字符变成3m
,3m
的最小操作次数是m,那么我们还多了一次删除。
所以当
b<=0
时,每个长为l
的字符串会需要l//3
次替换 - 对于
-
总结
总结一下整个逻辑:
- 当长度小于6时:次数为:
max(i,k)
,i为缺少字符数,k为缺少字符种类数 - 当长度大于等于6时:
- 首先计算超过的字符数
over
- 对于每个连续子串,当它的长度>=3时记录
(需要删除字符数d,替换次数r)
放入数组repeat
- 每次删除d个字符其实是为了减少一次替换,所以为了多减少替换,把d=1的优先于d=2的删除
- 不断删除,计算删除次数
delete
和替换次数replace
,直到over<=delete
或者len(repeat)==0
- 如果
over>delete
,说明还需要删除,此时每删除3个都能节省一次替换,我们算算最多需要减少的替换次数为:max(0,replace-k)
,至少要保留k次替换来满足情况二。而剩下的删除次数能减少的替换次数为:ceil((over-delete)/3)
,所以我们最终减少的替换次数为两者的最小值,假设为save
。 - 最终结果是删除
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)