LicenseKeyFormatting [source code]
public class LicenseKeyFormatting {
static
/******************************************************************************/
class Solution {
public String licenseKeyFormatting(String S, int K) {
assert S.length () > 0 && K > 0;
char[] chs = S.replace ("-", "").toUpperCase ().toCharArray ();
int i = 0;
StringBuilder sb = new StringBuilder ();
if (chs.length > K) {
int remainder = chs.length % K;
while (i < remainder)
sb.append (chs[i++]);
if (sb.length () > 0)
sb.append ('-');
while (i < chs.length) {
sb.append (chs[i]);
if ((i + 1 - remainder) % K == 0 && i < chs.length - 1)
sb.append ('-');
i++;
}
} else {
sb.append (chs);
}
return sb.toString ();
}
}
/******************************************************************************/
public static void main(String[] args) {
LicenseKeyFormatting.Solution tester = new LicenseKeyFormatting.Solution();
String[] inputs = {
"5F3Z-2e-9-w", "4", "5F3Z-2E9W",
"2-5g-3-J", "2", "2-5G-3J",
"2", "2", "2",
"a-a-a-a-", "1", "A-A-A-A",
};
for (int i = 0; i < inputs.length / 3; i++) {
String S = inputs[3 * i], ans = inputs[3 * i + 2];
int K = Integer.parseInt (inputs[3 * i + 1]);
System.out.println (Printer.separator ());
String output = tester.licenseKeyFormatting (S, K);
System.out.printf ("%s and %d-> %s, expected: %s\n",
S, K, Printer.wrapColor (output, output.equals (ans) ? "green" : "red"), ans
);
}
}
}
题目本身看起来很简单, 不过AC rate并不高, 结合Google一贯的尿性, 估计有一些暗坑test; 不过还是先写;
Start from concrete
处理边界的时候, 直接笔算一个trace作为参考; 这个也是提醒过自己好几次了; 我这里用K等于3, chs长度为11来进行计算;
题目本身不难, 就在于细节处理上要小心一点, 最后写的时候果然是各种暗坑; 要是实际在Google面试的时候, 我这个分量, 估计还是不够吃的. 最后的速度是
注意这个API的使用:
java> sb.append (new char[]{'3'})
java.lang.StringBuilder res2 = 123
本身简单的题目, 代码写的是否优雅就是比拼的对象了; 这个是discussion最优解:
@yuxiangmusic said in Java 5 lines clean solution:
public String licenseKeyFormatting(String s, int k) {
StringBuilder sb = new StringBuilder();
for (int i = s.length() - 1; i >= 0; i--)
if (s.charAt(i) != '-')
sb.append(sb.length() % (k + 1) == k ? '-' : "").append(s.charAt(i));
return sb.reverse().toString().toUpperCase();
}
这个sb.length() % (k + 1) == k ? '-' : ""
的使用非常的漂亮; 他的速度之所以快, 是因为他没有依赖于先找到S的长度, 而是直接1pass就搞定; 然而1pass做法的难度在于, 你怎么知道总长度呢? 解决这个问题的两个关键手段, 一个是反向扫, 一个是使用accumulator size, 这个其实是见到过的;
至于他这个巧妙的sb.length() % (k + 1) == k ? '-' : ""
, 你可以自己大概笔算一下(还是用"01234567890"这个例子, 不考虑'-')就知道是什么作用了;
看看这个trace:
i:(10), letter:(0) sb:(), sb.length:(0)
i:(9), letter:(9) sb:(0), sb.length:(1)
i:(8), letter:(8) sb:(09), sb.length:(2)
i:(7), letter:(7) sb:(098), sb.length:(3)
i:(6), letter:(6) sb:(0987), sb.length:(4)
i:(5), letter:(5) sb:(0987-6), sb.length:(6)
i:(4), letter:(4) sb:(0987-65), sb.length:(7)
i:(3), letter:(3) sb:(0987-654), sb.length:(8)
i:(2), letter:(2) sb:(0987-6543), sb.length:(9)
i:(1), letter:(1) sb:(0987-6543-2), sb.length:(11)
i:(0), letter:(0) sb:(0987-6543-21), sb.length:(12)
注意他这里的dash是作为一个prefix, 也就是先考虑是否append dash, 然后再append letter;
看看这个例子就知道为什么要%(k+1)
了, 因为从第二个dash开始的append的判断, 是判断当前sb里面是否正好有multiple 4(-4)+
这样的结构; 理解了这个正则, 这个问题就不难理解了; 当然, 对于第一个dash的理解, 也不难; 所以最后实际上就是判断4('-' 4)*
这个正则;
另外, 想到反向扫的做法, 本身也不是特别难; 这一题本质上还是一个类似余数的问题, 余数问题把remainder部分放在最后去处理是一个很自然的思路, 往往能够让代码简化很多, 就跟这一题一样;
可以看到, 反向扫的另外一个好处就是自动解决了chs.length <= K
这个问题;
这个是另外一个解法:
@zzhai said in Java 5 lines clean solution:
Found this little gem -
https://discuss.leetcode.com/topic/75225/java-easy-to-understand-solution
public String licenseKeyFormatting(String S, int K) {
S = S.replaceAll("-", "").toUpperCase();
StringBuilder sb = new StringBuilder(S);
// Starting from the end of sb, and going backwards.
int i = sb.length() - K;
while(i > 0) {
sb.insert(i, '-');
i = i - K;
}
return sb.toString();
}
这个小算法还真的是有点意思, 用gem来称呼完全不过分; 这个人看到了问题的核心本质, 本质不是build, 而是delete dash然后add dash;
这个是一个类似我的正向扫的做法, 不过他选择了用2pass, 虽然如此, 最后得到的代码非常容易理解, 而且不用专门处理K > length
的情况:
@hemantpandey said in Easy to understand using StringBuilder:
public class Solution {
public String licenseKeyFormatting(String S, int K) {
// Replacing all - and converting all letters to uppercase
String S1 = S.replace("-","");
S1 = S1.toUpperCase();
// Making stringBuilder
StringBuilder sb = new StringBuilder();
for(int i=0; i<S1.length();i++) {
sb.append(S1.charAt(i));
}
int len = sb.toString().length();
// Inserting '-' from back at every K position
for(int i=K; i < len; i=i+K) {
sb.insert(len-i,'-');
}
return sb.toString();
}
}
当然实际上第二个loop是没有必要的:
@zzzzzgo said in Easy to understand using StringBuilder:
@hemantpandey Maybe there is no need to go through S to build the StringBuilder? That will be more concise then.
public String licenseKeyFormatting(String S, int K) {
S = S.replace("-","").toUpperCase();
// Making stringBuilder
StringBuilder sb = new StringBuilder();
sb.append(S);
int len = S.length();
// Inserting '-' from back at every K position
for(int i=K; i < len; i=i+K) {
sb.insert(len-i,'-');
}
return sb.toString();
}
submission最优解: 10(99):
class Solution {
public String licenseKeyFormatting(String s, int k) {
if(s.isEmpty()) {
return s;
}
char[] cs = s.toCharArray();
int newLen = cs.length + cs.length / k;
char[] result = new char[newLen];
int i = cs.length, j = result.length;
Loop:
for(char c;;) {
for(int m = 0; m < k;) {
if(--i < 0) {
break Loop;
}
if((c = cs[i]) == '-') {
continue;
}
if(c >= 'a' && c <= 'z') {
c -= 32;
}
m++;
result[--j] = c;
}
result[--j] = '-';
}
if(j < result.length && result[j] == '-') {
j++;
}
return new String(result, j, result.length - j);
}
}
造轮子的算法而已, 连 StringBuilder都不肯用, 非要用个array来搞, 大概看看就行了;
Problem Description
You are given a license key represented as a string S which consists only alphanumeric character and dashes. The string is separated into N+1 groups by N dashes.
Given a number K, we would want to reformat the strings such that each group contains exactly K characters, except for the first group which could be shorter than K, but still must contain at least one character. Furthermore, there must be a dash inserted between two groups and all lowercase letters should be converted to uppercase.
Given a non-empty string S and a number K, format the string according to the rules described above.
Example 1:
Input: S = "5F3Z-2e-9-w", K = 4
Output: "5F3Z-2E9W"
Explanation: The string S has been split into two parts, each part has 4 characters.
Note that the two extra dashes are not needed and can be removed.
Example 2:
Input: S = "2-5g-3-J", K = 2
Output: "2-5G-3J"
Explanation: The string S has been split into three parts, each part has 2 characters except the first part as it could be shorter as mentioned above.
Note:
- The length of string S will not exceed 12,000, and K is a positive integer.
- String S consists only of alphanumerical characters (a-z and/or A-Z and/or 0-9) and dashes(-).
- String S is non-empty.
Difficulty:Easy
Total Accepted:25.1K
Total Submissions:61.4K
Contributor:aizj_Forever
Companies
google