# Strong password checker in Python

This is a Leetcode problem -

A password is considered strong if below conditions are all met -

1. It has at least 6 characters and at most 20 characters.

2. It must contain at least one lowercase letter, at least one uppercase letter, and at least one digit.

3. It must NOT contain three repeating characters in a row (...aaa... is weak, but ...aa...a... is strong, assuming other conditions are met).

Write a function strong_password_checker(s), that takes a string s as input, and returns the MINIMUM change required to make s a strong password. If s is already strong, return 0.

Insertion, deletion or replacements of any one character are all considered as one change.

Here is my solution to this challenge -

def strong_password_checker(s: str) -> int:
def has_lower(s):
for c in s:
if c.islower(): return 1
return 0

def has_upper(s):
for c in s:
if c.isupper(): return 1
return 0

def has_digits(s):
for c in s:
if c.isnumeric(): return 1
return 0

def find_repeats(s):
i = 0
j = 0
repeats = []
while i < len(s) - 1:
if s[i+1] == s[i]:
i += 1
continue
if (i - j + 1) > 2: repeats.append(i - j + 1)
i += 1
j = i
if (i - j + 1) > 2: repeats.append(i - j + 1)
return repeats

def repeats_after_delete(reps, d):
if d >= sum([r - 2 for r in reps]):
return []
reps = sorted(reps, key=lambda d: d%3)
while d > 0:
for i in range(len(reps)):
if reps[i] < 3:
continue
r = reps[i] % 3 + 1
reps[i] -= min(r, d)
d -= r
if d <= 0:
break
return [r for r in reps if r > 2]

def num_repeats_change(repeats):
return sum([r // 3 for r in repeats])

total_changes = 0
format_changes = (1 - has_lower(s)) + (1 - has_upper(s)) + (1 - has_digits(s))
repeats = find_repeats(s)
if len(s) < 6:
repeat_change = num_repeats_change(repeats)
total_changes = max([6 - len(s), format_changes, repeat_change])
elif len(s) > 20:
repeats = repeats_after_delete(repeats, len(s) - 20)
repeat_change = num_repeats_change(repeats)
total_changes = len(s) - 20 + max([repeat_change, format_changes])
else:
repeat_change = num_repeats_change(repeats)
total_changes = max([repeat_change, format_changes])


Here are some example outputs -

#print(strongPasswordChecker("aaaaaaaaaaaaAsaxqwd1aaa"))

>>> 6

#Explanation - aa1aa1aa1aAsaxqwd1aa (just an example - delete three characters, add three digits to replace consecutive a's)


#print(strongPasswordChecker("aaaaa"))

>>> 2

#Explanation - aaAaa1 (just an example - add a character (so the length is at least 6), add an uppercase letter to replace consecutive a's)


#print(strongPasswordChecker("aAsaxqwd2aa"))

>>> 0

#Explanation - A strong password (all conditions are met)


Here are the times taken for each output -

%timeit strongPasswordChecker("aaaaaaaaaaaaAsaxqwd1aaa")
>>> 18.7 µs ± 1.75 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)

>>> 5.05 µs ± 594 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

>>> 7.19 µs ± 469 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)


So, I would like to know whether I could make this program shorter and more efficient.

Any help would be highly appreciated.

• It really is too bad they called this a strong password checker. It might've been in the 80's, but certainly not anymore. – l0b0 May 29 '19 at 8:55

You can make it shorter: use any and int: for example:

password_contains_digits = int(any(c.isdigit() for c in s))


or use set:

password_set = set(s)
digits = set("1234567890")


or use RegEx:

password_contains_digits = len(re.findall(r'[0-9]', s)) != 0


if s.lower() == s: