算法导论:字符统计问题

  在ACM之家看到一道有趣的算法题,好多类似华为,Google等IT巨鳄,都以此为模板来考察实习生。

  (1)华为的考题形式是:通过键盘输入一串小写字母(a~z)组成的字符串,编写一个字符串过滤程序,若字符串中出现多个相同的字符,将非首次出现的字符过滤掉。如,输入字符串“abacacde”,则输出的过滤结果为“abcde”。

  不是很难的问题,采用枚举法,一个二重循环就可搞定。示例代码如下:

 1 package org.warnier.zhang.demo;
 2 
 3 public class StringFilter {
 4 
 5     public String filter(String origin) {
 6         String result;
 7         if (origin == null) {
 8             result = null;
 9         } else {
10             char[] chars = origin.toCharArray();
11             StringBuilder sb = new StringBuilder();
12 
13             for (int i = 0; i < chars.length; i++) {
14                 int j = 0;
15                 for (; j < i; j++) {
16                     if (chars[i] == chars[j]) {
17                         break;
18                     }
19                 }
20                 if (j == i) {
21                     sb.append(chars[i]);
22                 }
23             }
24             result = sb.toString();
25         }
26         return result;
27     }
28 }

  官网给出的解决方案更巧妙,如下翻译为了Java版:

 1   public String filter(String origin){
 2         String result;
 3         if(origin == null){
 4             result = null;
 5         }else{
 6             /**
 7              * 判断字符是否出现的标志;
 8              */
 9             boolean[] flags = new boolean[26];
10             for(int i = 0; i < flags.length; i++){
11                 flags[i] = false;
12             }
13             char[] chars = origin.toCharArray();
14             StringBuilder sb = new StringBuilder();
15             /**
16              * 判断字符是否第一次出现;
17              */
18             for(int i = 0; i < chars.length; i ++){
19                 if(false == flags[chars[i] - 'a']){
20                     flags[chars[i] - 'a'] = true;
21                     sb.append(chars[i]);
22                 }
23             }
24             result = sb.toString();
25         }
26         return result;
27     }

  (2)Google的考题形式是:在一个字符串中找到第一个只出现一次的字符,如输入abaccdeff,则输出b。

  如果提前参见过华为面试的实习生是不是就要幸运的多了,在没有算法复杂度的要求下,上面的代码简单修改一下就搞定了。如,  

 1   public String filter(String origin) {
 2         String result = null;
 3         if (origin != null) {
 4             char[] chars = origin.toCharArray();
 5             for (int i = 0; i < chars.length; i++) {
 6                 for (int j = 0; j < i; j++) {
 7                     if (chars[i] == chars[j]) {
 8                         chars[i] = '0';
 9                         chars[j] = '0';
10                         break;
11                     }
12                 }
13             }
14             for (int i = 0; i < chars.length; i++) {
15                 if (chars[i] != '0') {
16                     result = String.valueOf(chars[i]);
17                     break;
18                 }
19             }
20         }
21         return result;
22     }

  官网也给出了一种全新的解法,翻译为Java版如下:

 1 public String filter(String origin){
 2         String result = null;
 3         if(origin != null){
 4             /**
 5              * 保存字符出现的次数;
 6              */
 7             int[] flags = new int[26];
 8             for(int i = 0; i < flags.length; i++){
 9                 flags[i] = 0;
10             }
11             /**
12              * 统计字符出现的次数;
13              */
14             char[] chars = origin.toCharArray();
15             for(int i = 0; i < chars.length; i++){
16                 flags[chars[i] - 'a']++;
17             }
18             for(int i = 0; i < flags.length; i++){
19                 if(flags[i] == 1){
20                     result = String.valueOf((char)('a' + i));
21                     break;
22                 }
23             }
24         }
25         return result;
26     }

  其实,官网给出的答案不难看出与上面华为考题解答的相似处。这从另一个角度说明,这道考题虽然华为,Google的考察角度不同,但是万变不离其宗,解题的过程中要拨开迷雾,看清本质。

  再者,官网的解题策略也是一种巧妙的思路,我曾在自己的一篇博客“分享:GenericCalendar工具类”中,采用类似上述“判断字符是否出现”,“保存字符出现次数”标志量的方式处理平,闰年二月天数的选择问题。

原文地址:https://www.cnblogs.com/warnier-zhang/p/4485814.html