420. Strong Password Checker
不定期更新leetcode解题java答案。
采用pick one的方式选择题目。
本题为密码检测程序,给定字符串,要求1、字符串长度在6~20之间,2、字符串至少包含小写字母、大写字母和数字,3、字符串不允许存在3个连续的相同字符。(如:"aaa")
问:需要几次变换操作可以将给定字符串改写为规范密码字符串。变换操作包括:1、插入一个字符,2、删除一个字符,3、修改一个字符。
首先考虑长度问题,如果小于6个或者大于20个字符,每多一个字符必须做出一次处理,如果少则插入;多则删除。
然后考虑字符类型问题,每少一种字符类型则需要插入或者修改处理。
最后关于连字符串,则可以采用修改、插入、删除,三种方式来进行处理。
我们分析一下这三种方式对三种要求的影响:
1、对长度的关系是最直接的,小则加,大则删,N——N;变化不能增加长度。
2、若字符类型少,采用插入或者修改对其影响相同,N——N;删除不能增加类型。
3、若存在连续字符,试考虑对一个长度为N的连续字符,删除操作:重复字符长度减1,N——N-1;插入操作:重复字符长度减2,N——N-2;修改操作:重复字符长度-3,N——N-3。
有如上规则可以试想长度若小,则增加的时候尽量增加字符类型,并且由于插入时可以减少存在连续字符的长度,如:"aaaaab",插入后可变为"aaa1aab",由于本题中长度最低限度较小,偷懒没有将插入的分析加入(因为连续字符不满足要求的最小长度是3,这个东西可以看下文以及代码进行理解)。若长度较大,则进行删除操作,此时应尽可能满足删除连续字符长度为3的倍数中的字符,其次为连续字符长度为3的倍数+1中的字符(为了删除操作多时尽可能的减少之后的修改操作,后文会提到),最后是删除连续字符长度为3的倍数+2中的字符。
此时完成了长度要求,若长度要求满足,种类缺少,我们尽可能采用修改操作,不考虑插入操作。由于插入操作不只变换了长度(影响了第一步),而且插入操作减少连续字符的数量也没有修改操作多:N-2变化的小于N-3。
最后是对存在的连续字符进行操作,通过上述分析,很明显可以看出使用修改操作最小化操作次数。
由上可得如下代码:
1 public class Solution { 2 public int strongPasswordChecker(String s) { 3 boolean hasDigit = false, hasLowercase = false, hasUppercase = false; 4 5 char lastChar = ' '; 6 int count = 1; 7 ArrayList<Integer> repeatCounts = new ArrayList<Integer>(); 8 9 for(int i = 0; i < s.length(); i++){ 10 char tmpChar = s.charAt(i); 11 12 //判断是否存在要求的字符 13 if(tmpChar <= '9' && tmpChar >= '0') 14 hasDigit = true; 15 else if(tmpChar <= 'Z' && tmpChar >= 'A') 16 hasUppercase = true; 17 else if(tmpChar <= 'z' && tmpChar >= 'a') 18 hasLowercase = true; 19 20 //统计超过3连字符串的数量以及对应的个数 21 if(lastChar == tmpChar){ 22 count++; 23 }else{ 24 if(count >= 3) 25 repeatCounts.add(count); 26 27 count = 1; 28 } 29 lastChar = tmpChar; 30 } 31 //循环结束后没有处理当前统计长度 32 if(count >= 3) 33 repeatCounts.add(count); 34 35 //记录长度问题 36 int lengthDifference = 0; 37 if(s.length() > 20) 38 lengthDifference = s.length() - 20; 39 else 40 lengthDifference = s.length() < 6 ? s.length() - 6 : 0; 41 42 int steps = Math.abs(lengthDifference); 43 44 int types = (hasDigit ? 0 : 1) + (hasUppercase ? 0 : 1) + (hasLowercase ? 0 : 1); 45 46 //先满足长度要求 47 if(lengthDifference < 0){ 48 while(lengthDifference != 0){ 49 lengthDifference++; 50 types--; 51 //本题中如果长度小,那么插入一个即可消除全部3连的 52 repeatCounts.clear(); //这里偷懒了,如果字符串长度要求下限较大则应采用添加的方式进行处理,类同删除操作:条件稍微变化,N-1变为N-2 53 } 54 }else if(lengthDifference > 0){ 55 while(lengthDifference != 0){ 56 lengthDifference--; 57 if(repeatCounts.size() != 0){ 58 //删除操作 循环3次用于考虑先减哪种长度的连串 59 for(int i = 0; i < repeatCounts.size() * 3; i++){ 60 int loc = i % repeatCounts.size(); 61 if(repeatCounts.get(loc) % 3 == 0){ 62 repeatCounts.set(loc, repeatCounts.get(loc) - 1); 63 break; 64 } 65 if(i >= repeatCounts.size() && repeatCounts.get(loc) % 3 == 1 && repeatCounts.get(loc) > 3){ 66 repeatCounts.set(loc, repeatCounts.get(loc) - 1); 67 break; 68 } 69 if(i >= repeatCounts.size() * 2 && repeatCounts.get(loc) > 3){ 70 repeatCounts.set(loc, repeatCounts.get(loc) - 1); 71 break; 72 } 73 } 74 } 75 } 76 } 77 78 //长度满足要求考虑种类要求 79 while(types > 0){ 80 if(repeatCounts.size() != 0){ 81 for(int i = 0; i < repeatCounts.size(); i++){ 82 if(repeatCounts.get(i) >= 3){ 83 repeatCounts.set(i, repeatCounts.get(i) - 3); 84 break; 85 } 86 } 87 } 88 types--; 89 steps++; 90 } 91 92 for(int i = 0; i < repeatCounts.size(); i++){ 93 steps += repeatCounts.get(i) / 3; 94 } 95 96 return steps; 97 } 98 }