A password is considered strong if below conditions are all met:
- It has at least 6 characters and at most 20 characters.
- It must contain at least one lowercase letter, at least one uppercase letter, and at least one digit.
- 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 strongPasswordChecker(s), that takes a string s as input, and return the MINIMUM change required to make s a strong password. If s is already strong, return 0.
Insertion, deletion or replace of any one character are all considered as one change.
要求将一串弱密码改成强密码,即不能出现三个连续的字符,长度为6到20,且要同时有大写字母,小写字母和数字(记缺少种类个数为needs),求最少的操作数是多少。一次操作只能是增删改一个字符。因为一个操作可能使得同时满足几个条件,比如112aaa223,只需要改变aaaa中间的a为一个大写字母,就能满足条件,所以要考虑的比较多。这里按照字符串长度n,分三种情况考虑。
第一种情况是n<6。这种情况是肯定要添加字符操作的,而不需要删除操作。所以可以在添加字符的同时满足另外两个条件。计算needs和使得不出现连续三个字符的改变数chRepeat(对每个连续块每三个字符要修改一个,所以chRepeat=连续块大小/3,这里连续块指的是连续数大于2),因为这两个操作也是互相影响的,即在破坏三连续的同时通过修改字符满足符合种类要求,反过来也行,所以求needs和chRepeat的最大值即可。最后因为添加字符和上述两个操作也是互相影响的,所以求添加数和前面的最大值的最大值即可。
第二种情况是6<=n<=20。这种情况不需要添加和删除,像上面一样求needs和chRepeat的最大值即可。
第三种情况是n>20,比较复杂。这种情况需要删除字符,不需要添加。删除字符和添加不一样的是,要删除到只剩下两个连续字符才能破坏三连续,而且种类只能靠修改来获得。为了使得操作数最少,也就是使得删除字符到n=20后,需要修改的字符最少。从一个例子来看,aaaabbaaabbaaa123456A,n=21,只能删除一个字符,有三个连续块。如果删除4个a中的1个,后面还要修改3个连续块中的3个a,操作数为4;如果删除3个a中的1个,剩下2个连续块,修改2个a即可,操作数为3。所以优先修改(连续块大小%3)比较小的块会比较好。遵守这个原则,每个块删除到(连续块大小%3)=2就转向删除别的块(如果删除数还够的话)。直到删除数为0,统计剩下的连续块需要修改的次数,和needs比较取最大值(这两个操作还是互相影响的),加上删除数,就得到答案。
代码:
class Solution
{
public:
int strongPasswordChecker(string s)
{
int needUpper = 1, needLower = 1, needDigit = 1;
vector<int>repeat;
int cnt = 1, n = s.size(), needs = 0 ,chRepeat = 0;
if(n == 0) return 6;
for(int i = 0; i < n; ++i)
{
if(isupper(s[i])) needUpper = 0;
if(islower(s[i])) needLower = 0;
if(isdigit(s[i])) needDigit = 0;
if(i == 0) continue;
if(s[i] == s[i - 1])
{
++cnt;
}
else
{
if(cnt >= 3)
{
chRepeat += cnt / 3;
repeat.push_back(cnt);
}
cnt = 1;
}
}
if(cnt >= 3)
{
chRepeat += cnt / 3;
repeat.push_back(cnt);
}
needs = needUpper + needLower + needDigit;
if(n < 6)
{
return max(6 - n, max(chRepeat, needs));
}
else if(n <= 20)
{
return max(needs, chRepeat);
}
else
{
int r = n - 20;
chRepeat = 0;
auto cmp = [](const int n1, const int n2){return (n1 % 3) < (n2 % 3);};
sort(repeat.begin(), repeat.end(), cmp);
for(int i = 0; i < repeat.size(); ++i)
{
if(repeat[i] % 3 == 2) break;
int tmp = (repeat[i] % 3) + 1;
if(r >= tmp)
{
repeat[i] -= tmp;
r -= tmp;
}
else
{
repeat[i] -= r;
r = 0;
break;
}
}
for(int i = 0; i < repeat.size(); ++i)
{
int tmp = ((repeat[i] - 2) / 3) * 3;
if(r != 0 && r >= tmp)
{
r -= tmp;
}
else
{
chRepeat += (repeat[i] - r) / 3;
r = 0;
}
}
return n - 20 + max(needs, chRepeat);
}
}
};