Given a non-empty string containing an out-of-order English representation of digits 0-9
, output the digits in ascending order.
Note:
- Input contains only lowercase English letters.
- Input is guaranteed to be valid and can be transformed to its original digits. That means invalid inputs such as "abc" or "zerone" are not permitted.
- Input length is less than 50,000.
Example 1:
Input: "owoztneoer" Output: "012"
Example 2:
Input: "fviefuro" Output: "45"
这个题目咋看之下还是有点麻烦,网友提到用穷举加上回溯来做。但是针对这一题,由于题目里面已经交代了输入的序列一定是有效的,我想到一个非常巧妙的方法,不具有通用性。
思路:
由于字母的总数我们是知道的,所以开始我们可以用一个数组int[26]来记录下每个字母的数量,接下来我们根据这个,可以来推断每个单词有多少个。
仔细分析我们可以发现,有5个单词,都存在一个特定的字母,在这10个单词里面只有它自己有,比如:"zero"->z, "two"->w, "four"->u, "six"->x,"eight"->g,比如对于zero来说,z只有它有,其他的单词没有。换句话说,我只要知道一共有多少个z,就知道有多少个zero。同理,对于two,我只要知道有多少个w,就知道有多少个two。
所以,先算这个5个单词,这5个特定的字母,从int[26]里面获取特定字母的个数,然后将其他字母的数量减去这个数量就可以了。比如,先算zero,发现z有5个,然后最后的结果肯定有5个0,如果e有11个,那么11-5=6,算完zero以后,e就只有6个,以此类推即可。
当然,这前5个单词是顺序不分先后的。接下来,算的单词就必须保证顺序了,我们只要找到接下来拥有独一无的字母的单词就可以算了。比如,接下来算five,因为f只有four和five有,four在前面一句算过了,现在算five就好了。同理,后面的顺序是,five,seven,one,three,nine。
然后根据算出来的结果,每个数字对应几个,拼出结果就好。代码如下,复杂度大致为O(N),确实只需要一次遍历即可,这个方法打败了95.94%的提交:
public class Solution {
public String originalDigits(String s) {
StringBuilder sb = new StringBuilder();
char[] cArs = s.toCharArray();
int[] nArrChars = new int[26];
for (char c : cArs) {
nArrChars[c - 'a']++;
}
int[] nArrResult = new int[10];
// "zero"->z, "two"->w, "four"->u, "six"->x, "eight"->g
nArrResult[0] = nArrChars['z' - 'a'];
minusDigits(nArrChars, "zero", nArrResult[0]);
nArrResult[2] = nArrChars['w' - 'a'];
minusDigits(nArrChars, "two", nArrResult[2]);
nArrResult[4] = nArrChars['u' - 'a'];
minusDigits(nArrChars, "four", nArrResult[4]);
nArrResult[6] = nArrChars['x' - 'a'];
minusDigits(nArrChars, "six", nArrResult[6]);
nArrResult[8] = nArrChars['g' - 'a'];
minusDigits(nArrChars, "eight", nArrResult[8]);
// "five"->f, "seven"->v, "one"->o
nArrResult[5] = nArrChars['f' - 'a'];
minusDigits(nArrChars, "five", nArrResult[5]);
nArrResult[7] = nArrChars['v' - 'a'];
minusDigits(nArrChars, "seven", nArrResult[7]);
nArrResult[1] = nArrChars['o' - 'a'];
minusDigits(nArrChars, "one", nArrResult[1]);
// "three"->t, "nine"->i
nArrResult[3] = nArrChars['t' - 'a'];
minusDigits(nArrChars, "three", nArrResult[3]);
nArrResult[9] = nArrChars['i' - 'a'];
minusDigits(nArrChars, "nine", nArrResult[9]);
// add all number
for (int i = 0; i < nArrResult.length; i++) {
for (int j = 0; j < nArrResult[i]; j++) {
sb.append((char) (i + (int) '0'));
}
}
return sb.toString();
}
private void minusDigits(int[] nArr, String str, int value) {
char[] cArr = str.toCharArray();
for (char c : cArr) {
nArr[c - 'a'] -= value;
}
}
}
以上代码有些冗余,实际上可以把这样的序列存起来,用一个循环解决就好,如下:
public class Solution {
private static final String[] NUMBERS_STRING = { "zero", "two", "four", "six", "eight", "five", "seven", "one",
"three", "nine" };
private static final char[] DIGITS = { 'z', 'w', 'u', 'x', 'g', 'f', 'v', 'o', 't', 'i' };
private static final int[] NUMBERS = { 0, 2, 4, 6, 8, 5, 7, 1, 3, 9 };
public String originalDigits(String s) {
StringBuilder sb = new StringBuilder();
char[] cArs = s.toCharArray();
int[] nArrChars = new int[26];
for (char c : cArs) {
nArrChars[c - 'a']++;
}
int[] nArrResult = new int[10];
for (int i = 0; i < 10; i++) {
nArrResult[NUMBERS[i]] = nArrChars[DIGITS[i] - 'a'];
minusDigits(nArrChars, NUMBERS_STRING[i], nArrResult[NUMBERS[i]]);
}
// add all number
for (int i = 0; i < nArrResult.length; i++) {
for (int j = 0; j < nArrResult[i]; j++) {
sb.append((char) (i + (int) '0'));
}
}
return sb.toString();
}
private void minusDigits(int[] nArr, String str, int value) {
char[] cArr = str.toCharArray();
for (char c : cArr) {
nArr[c - 'a'] -= value;
}
}
}
这里使用StringBuilder进行字符串拼接当然会效率更高。