[关闭]
@NewWorld 2016-04-10T11:31:55.000000Z 字数 4058 阅读 1697

LeetCode: Bulls and Cows

LeetCode


论文暂告一段落,闲着无聊到 LeetCode 刷刷题,蛤蛤蛤。

题目链接:Bulls and Cows

题目大意如下:

先给出一个数字串secret,再给出一个长度一样的猜测数字串guess。

对guess某个位置上的数字,若在secret串中的相同位置上存在相同的数字,则输出 1A,若有两个这种情况,则输出 2A;

当猜测数字串中除去完全匹配上位置的数字外,还有只猜中数字的情况,则猜中一个记为 1B,猜中两个记为 2B,secret中的数字不能被计数超过一次。

example 1 :
Secret number: "1807"
Friend's guess: "7810"
Output: 1A3B

example 2 :
Secret number: "1123"
Friend's guess: "0111"
Output: 1A1B



为了达到 excited 的效果,当然是挑 easy 做,第一版代码如下:

  1. // Version 汪
  2. public static String getHint1(String secret, String guess) {
  3. Set<Integer> indexListA = new HashSet<>();
  4. Set<Integer> indexListB = new HashSet<>();
  5. int length = guess.length();
  6. for (int i=0; i<length; i++) {
  7. if ((secret.charAt(i)^guess.charAt(i)) == 0) {
  8. indexListA.add(i);
  9. }
  10. }
  11. for (int i=0; i<length; i++) {
  12. if (indexListA.contains(i)) {
  13. continue;
  14. }
  15. for (int j=0; j<length; j++) {
  16. if (indexListA.contains(j) || indexListB.contains(j)) {
  17. continue;
  18. }
  19. if ((secret.charAt(i)^guess.charAt(j)) == 0) {
  20. indexListB.add(j);
  21. break;
  22. }
  23. }
  24. }
  25. return indexListA.size()+"A"+indexListB.size()+"B";
  26. }

将运行过程分为了两部分,第一部分针对完全匹配的情况,第二部分针对只猜对数字的情况。提交以后运行效果惨不忍睹,与主流运行效果形成了强烈的反差(一开始可是用的 ArrayList)。进行改进,琢磨着去掉老是去 HashSet 中进行查询的行为,得到第二版:

  1. // Version 吐
  2. public static String getHint2(String secret, String guess) {
  3. char[] secrets = secret.toCharArray();
  4. char[] guesses = guess.toCharArray();
  5. int numA = 0, numB = 0;
  6. int length = guess.length();
  7. for (int i=0; i<length; i++) {
  8. if (secrets[i] == guesses[i]) {
  9. numA++;
  10. secrets[i] = 'a';
  11. guesses[i] = 'a';
  12. }
  13. }
  14. for (int i=0; i<length; i++) {
  15. if (secrets[i] == 'a') {
  16. continue;
  17. }
  18. for (int j=0; j<length; j++) {
  19. if (guesses[j] == 'a') {
  20. continue;
  21. }
  22. if (secrets[i] == guesses[j]) {
  23. numB++;
  24. secrets[i] = 'a';
  25. guesses[j] = 'a';
  26. break;
  27. }
  28. }
  29. }
  30. return numA+"A"+numB+"B";
  31. }

主要是将字符串转为了一个字符数组,在字符数组中动手脚,标记已经匹配过的数字。然而提交后运行效果还是不理想。接着改进,发现第一部分发现完全匹配后,完全可以将匹配上的的数字丢弃掉,得到第三版:

  1. // Version 3
  2. public static String getHint3(String secret, String guess) {
  3. char[] secrets = secret.toCharArray();
  4. char[] guesses = guess.toCharArray();
  5. int numA = 0, numB = 0;
  6. int length = guesses.length;
  7. for (int i=0; i<length; i++) {
  8. while (secrets[i] == guesses[i]) {
  9. numA++;
  10. if (i == length-1) {
  11. length--;
  12. break;
  13. }
  14. secrets[i] = secrets[--length];
  15. guesses[i] = guesses[length];
  16. }
  17. }
  18. if (length == 0) {
  19. return numA+"A0B";
  20. }
  21. for (int i=0; i<length; i++) {
  22. for (int j=0; j<length; j++) {
  23. if (guesses[j] == 'a') {
  24. continue;
  25. }
  26. if (secrets[i] == guesses[j]) {
  27. numB++;
  28. secrets[i] = 'a';
  29. guesses[j] = 'a';
  30. break;
  31. }
  32. }
  33. }
  34. return numA+"A"+numB+"B";
  35. }

这样在完全匹配后,同时减少了 secret 串和 guess 串的长度,后面的查找次数被缩短了。突然发现,既然第一部分可以丢弃数字,第二部分也可以丢弃,得到第四版:

  1. // Version 4
  2. public static String getHint4(String secret, String guess) {
  3. char[] secrets = secret.toCharArray();
  4. char[] guesses = guess.toCharArray();
  5. int numA = 0, numB = 0;
  6. int length = guesses.length;
  7. for (int i=0; i<length; i++) {
  8. while (secrets[i] == guesses[i]) {
  9. numA++;
  10. if (i == length-1) {
  11. length--;
  12. break;
  13. }
  14. secrets[i] = secrets[--length];
  15. guesses[i] = guesses[length];
  16. }
  17. }
  18. if (length == 0) {
  19. return numA+"A0B";
  20. }
  21. int lengthS = length;
  22. for (int i=0; i<length; i++) {
  23. for (int j=0; j<lengthS; j++) {
  24. if (secrets[j] == guesses[i]) {
  25. numB++;
  26. lengthS--;
  27. if (lengthS == 0) {
  28. return numA+"A"+numB+"B";
  29. }
  30. secrets[j] = secrets[lengthS];
  31. break;
  32. }
  33. }
  34. }
  35. return numA+"A"+numB+"B";
  36. }

主要的改进就是在第二部分中丢弃 secret 串中已经匹配上的数字。这次提交后击败了30%左右的 javasubmissions,然而和第一仍然相差甚远。一定还有更好的方法,接着思索,发现在第二部分的时候已经不需要顾忌数字在串中的位置了,那么,叮!直接计算每个串中数字出现的次数,然后取较小就行。而且关键是数字只有 0-9,哈哈,那就好办了。

  1. // Versino 5
  2. public static String getHint5(String secret, String guess) {
  3. char[] secrets = secret.toCharArray();
  4. char[] guesses = guess.toCharArray();
  5. int numA = 0, numB = 0;
  6. int length = guesses.length;
  7. for (int i=0; i<length; i++) {
  8. while (secrets[i] == guesses[i]) {
  9. numA++;
  10. if (i == length-1) {
  11. length--;
  12. break;
  13. }
  14. secrets[i] = secrets[--length];
  15. guesses[i] = guesses[length];
  16. }
  17. }
  18. if (length == 0) {
  19. return numA+"A0B";
  20. }
  21. int[] countS = new int[10];
  22. int[] countG = new int[10];
  23. for (int i=0; i<length; i++) {
  24. countS[secrets[i]-'0']++;
  25. countG[guesses[i]-'0']++;
  26. }
  27. for (int i=0; i<countS.length; i++){
  28. numB += countS[i] > countG[i] ? countG[i] : countS[i];
  29. }
  30. return numA+"A"+numB+"B";
  31. }

麻蛋,猛然发现其实可以这样,不折腾...
运行效果与第五版相同。果然还是太 naive 啊。

  1. // Version 6
  2. public static String getHint6(String secret, String guess) {
  3. char[] secrets = secret.toCharArray();
  4. char[] guesses = guess.toCharArray();
  5. int numA = 0, numB = 0;
  6. int length = guesses.length;
  7. for (int i = 0; i < length; i++) {
  8. if (secrets[i] == guesses[i]) {
  9. numA++;
  10. }
  11. }
  12. int[] countS = new int[10];
  13. int[] countG = new int[10];
  14. for (int i = 0; i < length; i++) {
  15. countS[secrets[i] - '0']++;
  16. countG[guesses[i] - '0']++;
  17. }
  18. for (int i = 0; i < countS.length; i++) {
  19. numB += countS[i] > countG[i] ? countG[i] : countS[i];
  20. }
  21. return numA + "A" + (numB - numA) + "B";
  22. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注