一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。
如果一个密码满足下述所有条件,则认为这个密码是强密码:
- 由至少 6 个,至多 20 个字符组成。
- 至少包含 一个小写 字母,一个大写 字母,和 一个数字 。
- 同一字符 不能 连续出现三次 (比如 "...aaa..." 是不允许的, 但是 "...aa...a..." 如果满足其他条件也可以算是强密码)。
给你一个字符串 password ,返回 将 password 修改到满足强密码条件需要的最少修改步数。如果 password 已经是强密码,则返回 0 。
在一步修改操作中,你可以:
- 插入一个字符到 password ,
- 从 password 中删除一个字符,或
- 用另一个字符来替换 password 中的某个字符。
示例 1:
输入:password = "a"
输出:5
示例 2:
输入:password = "aA1"
输出:3
示例 3:
输入:password = "1337C0d3"
输出:0
提示:
1 <= password.length <= 50password由字母、数字、点'.'或者感叹号'!'
超过20一定要删除,怎么删 ? 以下过程用表格图示
-
333a3333a33333aAaAa333333 长度25,需要删除5次,优先删除连续3+次字符
-
只 替换 需要 333->1 + 3333->1 + 33333->1 + 333333 -> 2 次
-
第一轮删除:
- 长度是3倍数的连续字符(333和333333)各删除1次
- 替换 需要 33->0 + 3333->1 + 33333->1 + 33333 -> 1 次
- 本轮,共删除 2 次,替换少2次,两者置换比1:1
-
第二轮删除:还需要删除 5 - 2 = 3 次
- 长度除3余数为1连续字符(3333)各删除2次(2字符)
- 替换 需要 33->0 + 33->0 + 33333->1 + 33333 -> 1 次
- 本轮,共删除 2 次,替换少1次,两者置换比2:1
-
第三轮删除:还需要删除 5 - 2 - 2 = 1 次
- 剩余都是长度除3余数为2的连续字符(33333)各删除3次 (3字符)
- 替换 需要 33->0 + 33->0 + 33->0 + 33 -> 0 次
- 本轮,共删除 6次,替换 少 2次,两者置换比3:1
-
删多了!回到第三轮删除:还需要删除1次
- 删除6次删多了,只能删1次,把其中一个33333->3333
- 替换 需要 33->0 + 33->0 + 3333->1 + 33333 -> 1 次
- 本轮需要删除3:1置换替换,删不够,替换次数不变
-
-
三轮删除后为33a33a3333aAaAa33333,已删除5次,再替换2次,共7次即解
/**
* @param {string} password
* @return {number}
*/
var strongPasswordChecker = function (password) {
const n = password.length
let needLowerCase = needUpperCase = needNumber = 1 // 需要小写字母,大写字母和数字
for (let i = 0; i < n; i++) {
const charCode = password.charCodeAt(i)
if (charCode >= 97 && charCode <= 122) needLowerCase = 0 // 已有小写字母
else if (charCode >= 65 && charCode <= 90) needUpperCase = 0 // 已有大写字母
else if (charCode >= 48 && charCode <= 57) needNumber = 0 // 已有数字
}
const needLetter = needLowerCase + needUpperCase + needNumber // 缺少字符数
let delOne = delTwo = replace = 0
for (let i = 2; i < n; i++) { // 遍历找长度 >= 3 的连续字符串
if (password[i - 2] === password[i - 1] && password[i - 1] === password[i]) {
let len = 3
while (i + 1 < n && password[i + 1] === password[i++]) len++ // 连续字符串长度
replace += len / 3 | 0 // 每 3 个字符替换 1 个字符,即可中断 3 个字符的连续
if (len % 3 === 0) delOne++ // 长度是 3 的倍数的字符串数(需删 1 字符)
else if (len % 3 === 1) delTwo++ // 长度 / 3 余数 1 的连续字符串数(需删 2 字符)
} // 长度是 3 倍数,删 1 字符 => 长度 / 3 余数 2 字符串
}// 长度 / 3 余数 1, 删 2 字符 => 长度 / 3 余数 2 字符串,所以,剩下都是余数 2 字符串
if (n < 6) return Math.max(6 - n, needLetter) // 不够 6,优先插入缺少字符
if (n <= 20) return Math.max(replace, needLetter) // 长度合适,优先用缺少字符替换
const needDel = n - 20 // 请参考上面的三轮删除的说明
replace -= Math.min(needDel, delOne) // ↓ 如果还需要删除次数,不够删,按 2:1 置换
if (needDel - delOne > 0) replace -= Math.min((needDel - delOne) / 2 | 0, delTwo)
if (needDel - delOne - delTwo * 2 > 0) replace -= (needDel - delOne - delTwo * 2) / 3 | 0
return needDel + Math.max(replace, needLetter)
};