思路:
有三个问题需要解决:
1、缺失字符种类问题(大写,小写、数字) 可用操作:replace,insert
2、长度问题 可用该操作:insert(< 6),delete(> 20)
3、重复问题 可用操作: replace,insert,delete,其中replace的解决方法最优。
可以看到各种问题之间是有重复性的,也就是说可以通过解决某个问题,同时解决另一个问题。
我们试图找到一种途径能够在解决一个问题的同时解决(或者说缓解)其他问题。
根据观察:
长度小于6时,解决问题2的同时,可以解决(缓解)1,3,因此先考虑2,再利用2解决1,3。
长度大于20时,解决问题1可以缓解3(delete),解决问题2也可以缓解3(replace),由于replace操作并不能减少delete操作的步数,而delete操作可以减少(缓解)replace的操作步数,delete应当优先于replace操作(比如……AAA,先delete成……AA,就可以减少一步replace的操作了)。
长度<=20,>=6时,可通过解决1来解决3(优先replace)。
因此我们分情况讨论:
首先对于小于6的情况,必须的操作为insert。 根据推理,需要的操作步数为: step = 缺失字符种类数 + 字符串长度 > 6 ? 缺失字符种类数 : 6 - 字符串长度
对于大于20的情况,我们首先先解决多余出来的字符,delete操作必须要保证能最大限度地缓解问题,也就是使得之后重复字符串中需要replace的步数最少。我们尽量保证所有连续的字符串能变成(长度 % 3) = 2的状态。我们首先通过一步delete使得将每个长度为3的倍数的连续字符串变成 (长度 % 3) = 2的状态,然后再通过两步delete,使得每个 (长度 % 3) = 1 的连续字符串变成 (长度 % 3) = 2的状态,再通过三步delete 使得每个 (长度 % 3) = 2 的连续字符串变成 (长度 % 3) = 2的状态。
完成delete后由于长度变成了<=20. 我们可以将问题转化成<=20,>=6的情况:
检查问题1,计算出所需要的解决步骤step1,检查是否有问题3,计算出需要的解决步骤step3, 若step3 <= step1, 则我们只要解决问题1就能彻底解决问题3,因此最后的step = step1;若step3 > step1, 则说明,解决问题1能解决部分问题3,最后所需step = step3;
以下为代码部分:
class Solution {
public:
int strongPasswordChecker(string s) {
bool upper = false, lower = false, digit = false;
int step = 0;
vector<int> repeat;
int start = 0;
int size = s.size();
for (int i = 0; i < s.size(); i++) {
if (s[i] >= 'A' && s[i] <= 'Z')
upper = true;
else if (s[i] >= 'a' && s[i] <= 'z' )
lower = true;
else if (s[i] >= '0' && s[i] <= '9')
digit = true;
start = i;
while (i < s.size() - 1 && s[i] == s[i + 1]){
i++;
}
if (i - start >= 2)
repeat.push_back(i - start + 1);
}
if (upper && lower && digit && repeat.size() == 0 && size <= 20 && size >= 6) return 0;
int must = 0;
if (!upper) must++;
if (!lower) must++;
if (!digit) must++;
step += must;
if (size < 6) {
if (size + must < 6) step = 6 - size;
}
else {
// 解决 overLength 的问题(>20)
int overLen = size - 20 > 0 ? size - 20 : 0;
for (int i = 0; i < 3 && overLen; i++) {
for (vector<int>::iterator it = repeat.begin(); it != repeat.end() && overLen; ) {
if ( *it % 3 == i) {
int tmp = ((i + 1) > overLen) ? overLen : (i + 1);
*it -= tmp;
overLen -= tmp;
step += tmp;
if (*it < 3) {
it = repeat.erase(it);
continue;
}
}
it++;
}
}
step += overLen;
//此时长度 >= 6, <= 20
int left = 0;
for (auto i : repeat)
left += i / 3;
step += left > must ? left - must : 0;
}
return step;
}
};