1. 问题描述:
一个强密码应满足以下所有条件:
由至少6个,至多20个字符组成。
至少包含一个小写字母,一个大写字母,和一个数字。
同一字符不能连续出现三次 (比如 "...aaa..." 是不允许的, 但是 "...aa...a..." 是可以的)。
编写函数 strongPasswordChecker(s),s 代表输入字符串,如果 s 已经符合强密码条件,则返回0;否则返回要将 s 修改为满足强密码条件的字符串所需要进行修改的最小步数。
插入、删除、替换任一字符都算作一次修改。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/strong-password-checker
2. 思路分析:
分析题目可以知道我们需要根据字符串的长度分情况讨论,这里涉及的情况还是比较多的,当长度小于等于20的时候会比较好处理一点,长度大于20之后会比较恶心,下面是分情况讨论的情况:
由题目可以知道必须满足三个条件:① 长度大于等于6且小于等于20 ② 包含3类字符 ③ 不能有连续三个相同的字符
规定k为字符串的种类数,n为字符串长度
- n <= 5的时候为max{6 - n, 3 - k}
长度小于等于4的时候肯定是正确,当n = 5的时候分为 k = 1和k = 2,k = 1的时候那么可以修改最中间的一个字符使得连续三个的字符都是不一样的,末尾在加上一个字符,当k = 2的时候也是正确的
- 6 <= n <= 20的时候
长度小于等于20的时候说明满足①条件,所以只需要看第②个条件与第③个条件,我们需要先需要找到当前字符相同的一个区间[i,j],写出几个简单的例子可以发现插入操作次数:(s - 1) / 2,删除操作次数为s - 2,修改为s / 3,s为区间的长度,可以发现当n大于等于6的时候修改的操作次数是最少的,所以我们对6 <= n <= 20时候使用修改操作即可,那么可以得到总的操作次数为max{3 - k, p},p为连续的区间需要操作修改的操作次数
- n > 20
当字符串的长度大于20之后那么第①个条件已经不满足了,所以我们必须要删除掉n - 20个字符,所以这个是最少的操作次数,除了删除操作我们可能还需要对长度大于等于3的连续出现相同的字符进行修改,为了使得操作次数最少我们需要在删除字符的时候需要尽量使得删除之后修改字符的次数也尽量少,也即删除字符的时候减少修改的次数,对于区间长度大于等于3的连续出现相同的字符主要分为三种情况,区间长度模3的余数分为是0,1,2,可以发现当区间长度模3的余数等于的0的时候删除一个字符的时候那么是可以减少一次操作次数的,而余数为1的时候那么需要删除2个字符才可以减少一次修改次数,余数为2的时候需要删除3个字符才可以减少修改一次字符的操作,所以我们需要统计余数为0,余数为1,余数为2的区间个数,使用p来记录一开始字符串长度为n的总的修改次数,使用长度为3的列表来统计余数为0,1,2对应的情况,我们在删除的字符的时候尽量先删除余数为0的情况,这样余数为0的区间有d[0]个的时候那么我们每一次删除区间中一个字符的时候那么就可以减少一次操作次数,那么总的修改次数就需要减去这么多次,当我们删除余数为0的区间一个字符之后那么当前的区间就变为了第三种情况,也即区间长度模3的长度等于2,对于余数为2的情况也是类似的,这里是删除两个字符减少一次操作次数,每个连续相同字符的区间删除2个字符之后那么最后也会转换为第三种情况,每一次删除的时候都需要更新总的修改操作次数以及待删除的字符个数,最终只剩下第三种情况,那么我们就需要计算一下剩下来需要操作多少次即可。
3. 代码如下:
class Solution:
def strongPasswordChecker(self, password: str) -> int:
# a,b,c分别表示小写字母, 大写字母, 数字的种类数目
a, b, c = 0, 0, 0
k = 0
n = len(password)
# k表示不同种类的字符数目
for i in range(len(password)):
if "a" <= password[i] <= "z":
a = 1
elif "A" <= password[i] <= "Z":
b = 1
elif "0" <= password[i] <= "9":
c = 1
k = a + b + c
# 分为长度小于6与大于等于6的情况
if n < 6:
return max(3 - k, 6 - n)
else:
# p表示需要修改的次数
p = 0
d = [0] * 3
i = 0
while i < n:
j = i
while j < n and password[j] == password[i]:
j += 1
# 连续相等一段的长度
t = j - i
# 累加修改的次数
p += t // 3
# 对应的余数位置上加上1
if t >= 3:
d[t % 3] += 1
i = j
if n <= 20: return max(3 - k, p)
res = n - 20
# dels表示当前需要删除的字符个数
dels = n - 20
if d[0] and dels > 0:
# 余数为0的情况, 删除的字符应该是两者的最小值, 当删除掉每一段余数相同的区间之后
# 应该减去对应删除字符的个数以及减少需要修改的操作次数
t = min(d[0], dels)
dels -= t
p -= t
if d[1] and dels:
# 余数为2的时候那么需要减少t个字符, t个字符减少的操作次数为t // 2
t = min(d[1] * 2, dels)
dels -= t
p -= t // 2
if p and dels:
# 注意是p * 3
t = min(p * 3, dels)
p -= t // 3
# 返回一开始需要删除的字符个数加上最少的修改次数
return res + max(3 - k, p)
# FFFFFFFFFFFFFFF11111111111111111111AAA