420. Strong Password Checker
阿新 • • 發佈:2019-01-13
解法
參考這裡
主要是貪心= =
- 長度在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)