【问题标题】:How to find the first character in a given string, that appears only once [closed]如何找到给定字符串中的第一个字符,它只出现一次[关闭]
【发布时间】:2013-02-17 15:06:17
【问题描述】:

这是一道面试题。找到给定字符串中的第一个字符,它只出现一次(我想解决方案应该是 Java)。

例如:

"babcbcd" -> 'a' // 'a' 和 'd' 都只出现一次,但 'a' 出现在 'd' 之前

简单的解决方案是

  • 建立一个地图(例如HashMap):char -> 它在字符串中出现的次数;
  • 扫描字符串并对照map测试字符串字符,直到字符的value为1。

这有意义吗?最好的 map 实现是什么?有没有更好、更有效的解决方案?

【问题讨论】:

  • “有没有更好、更有效的解决方案?”怎么看?
  • 有没有更好、更有效的解决方案?我不这么认为。
  • 你可以保留一个优先队列并弹出顶部并检查它是否为 1。
  • @AramKocharyan 你用什么优先级?它比使用 hashmap 的双迭代更好吗?
  • 我认为这通常是您必须首先细化要求的问题。例如:内存或cpu时间更重要。我也认为,这里有点离题(programmers.stackexchange.com?)

标签: java string algorithm


【解决方案1】:

这是 Haskell 中的一个版本。

import Data.List (elemIndices)
firstSingle str = take 1 [a | a <- str, length (elemIndices a str) == 1]

*Main Data.List> firstSingle "babcbcd"
“一个”

【讨论】:

  • @RyanStewart ...你启发了我 ;)
  • 大声笑,反之亦然。查看my update。我一直在想一定有一个更简单的方法。
  • @RyanStewart 不错。击败你一个字符(不计算空格);)哦,但是有导入,哦,好吧
【解决方案2】:

在Java 中,您可以使用LinkedHashMap&lt;Character,Integer&gt; 来计算每个字符在字符串中出现的次数。由于LinkedHashMap 保留了插入顺序,因此您可以遍历其条目以找到恰好出现一次的第一个字符。

在合理的假设下,这将给出O(n) 解决方案。

【讨论】:

    【解决方案3】:

    如果我在面试,我会希望(虽然不一定期待)这样的事情:

    def string = 'babcbcd'
    def singles = string.toList().groupBy{ it }.findAll{ it.value.size() == 1 }.collect{ it.key }
    println "First char that appears once: " + string.find{ singles.contains it }
    

    关键是中间线。它将字符串作为字符列表,groups the list 逐个字符,这样您就可以过滤掉没有恰好发生一次的任何内容,最后生成出现过一次的字符列表。然后我们只需在字符串中搜索该列表中的第一个字符。

    它是 Groovy,因为在 Java 中不可能如此优雅。也许一旦 JDK 8 终于成功了……

    更新:一个更简洁的版本,灵感来自@groovy's Haskell solution。我现在几乎为我的第一个笨拙而感到尴尬:)

    def firstUnique(seq) { seq.findAll{ seq.count(it) == 1 }.first() }
    

    【讨论】:

      【解决方案4】:

      您可以在原始字符串的一次传递中完成:制作一个链接的哈希映射,存储您到目前为止找到的字符的计数。然后浏览地图条目(它将按插入顺序排列,因为它是一个链接地图)并在看到计数为 1 时停止。

      【讨论】:

        【解决方案5】:

        您已经找到了一个很好的解决方案,但如果您愿意,我建议使用不同的方法:

        final String abc = "abcdefg....z";
        boolean scanned[] = new boolean[abc.lenght()];
        //set all to false ...
        for(int i = 0; i<yourString.lenght(); i++){
            char c = yourString.charAt(i);
            if(!scanned[abc.indexOf(c)]){
                for(int j=i+1; j<yourString.lenght(); j++)
                    if(yourString.charAt(i) == c){ // founded another
                        scanned[abc.indexOf(c)] = true;
                        break;
                    }
                if(!scanned[abc.indexOf(c)])
                    return c;
            }
        }
        

        【讨论】:

          【解决方案6】:

          假设String 仅包含 a-z 字符,您可以对目前看到的字符使用队列并保持计数。

          public static String findFirstLetterThatAppearsOnceInString(String str) {
              int[] seenCount = new int[26];
              int[] queue = new int[26];
              int queueSize = 0;
              for (char c : str.toCharArray()) { // Iterate over the input characters
                  int i = c-'a';
                  if (seenCount[i]<2) {
                      seenCount[i]++;
                      // If seen for the first time, store it in queue
                      if (seenCount[i]==1) queue[queueSize++]=i;
                  }
              }
              for (int qi=0;qi<queueSize;qi++) if (seenCount[queue[qi]]==1) return (char)(queue[qi]+'a')+"";
              return null;
          }
          

          【讨论】:

            【解决方案7】:

            简单的非地图解决方案,假设没有 unicode:

            public String firstLonelyChar(String input)
            {
                while(input.length() > 0)
                {
                    int curLength = input.length();
                    String first = String.valueOf(input.charAt(0));
                    input = input.replaceAll(first, "");
                    if(input.length() == curLength - 1)
                        return first;
                }
                return null;
            }
            

            【讨论】:

              【解决方案8】:

              所有上述解决方案都需要 O(n) 内存。为了在 O(1) 内存中执行此操作,您可以对所有字符运行 for 循环(在 ASCII 中有 128 个)并计算出现次数和第一个外观,然后找到第一个非重复字符。时间复杂度 O(128|s|)。

              int min=Integer.MAX_VALUE;
              char ans=' '; //any initialization 
              for ( i=0; i<128;i++){
                  int count=0,first=-1;
                  for(j=0;j<s.length();j++){
                       if(s.charAt(j)==(char)(i)) count++;
                       if(count==1) first=j;
                       if(count>1) break;
                  }
                  if(count==1 && min>first){
                       first=min;ans=s.charAt(first);
                  }
              }
              if(min!=Integer.MAX_VALUE) System.out.println(ans);
              else System.out.println("No such char found");
              

              【讨论】:

              • 在 unicode 中,有很多超过 256 个字符。您的解决方案在内存方面并不比链接的哈希映射好多少。是的,您没有维护映射结构的管理开销,但在最坏的情况下,您的解决方案和哈希映射都需要一个 Integer 计数器来处理每个可能的字符。更糟糕的是:hashmaps 只需要一个字符对应一个字符,而你的 hashmaps 需要一个字符对应字母表中的每个字符 - 对于像 unicode 这样的大字母表来说很多。
              • @us2012:看看我是如何在 O(1) 内存中完成的。 Ans 是的,它应该是 ASCII 。谢谢你。我已经更正了。
              猜你喜欢
              • 2012-01-05
              • 1970-01-01
              • 2013-10-04
              • 2011-03-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-02-02
              相关资源
              最近更新 更多