【问题标题】:Easiest way of checking if a string consists of unique letters?检查字符串是否由唯一字母组成的最简单方法?
【发布时间】:2011-01-27 23:20:54
【问题描述】:

如果一个单词由唯一的字母组成(不区分大小写),我需要检查 Java。由于直接的解决方案很无聊,我想出了:

  1. 对于字符串中的每个字符,检查是否indexOf(char) == lastIndexOf(char)
  2. 将所有字符添加到 HashSet 并检查设置大小 == 字符串长度。
  3. 将字符串转换为 char 数组,按字母顺序排序,遍历数组元素并检查是否c[i] == c[i+1]

目前我最喜欢#2,似乎是最简单的方法。还有其他有趣的解决方案吗?

【问题讨论】:

    标签: java algorithm string


    【解决方案1】:

    我喜欢 HashSet 的想法。它在概念上很简单,并且只有一个通过字符串。对于简单的性能改进,请检查 add 返回值。您应该注意的一件事是,这是通过案例折叠来实现的。在一个方向。您可以使用不同的 equals 语义围绕 Character 创建一个包装类,以便真正区分大小写。

    有趣的是,Apache Commons 有一个 CaseInsensitiveMap (src),它通过大写然后小写的键来工作。您可能知道,Java 的 HashSet 由 HashMap 支持。

    public static boolean allUnique(String s)
    {
      // This initial capacity can be tuned.
      HashSet<Character> hs = new HashSet<Character>(s.length());
      for(int i = 0; i < s.length(); i++)
      {
        if(!hs.add(s.charAt(i).toUpperCase())
          return false;
      }
      return true;
    }
    

    【讨论】:

      【解决方案2】:
                 import java.io.*;
      
                         class unique
                        {
                                 public static int[] ascii(String s)
                                 {
                                          int length=s.length();
                                          int asci[] = new int[length];
                                          for(int i=0;i<length;i++)
                                          {
                                                    asci[i]=(int)s.charAt(i);
                                           }
                                    return asci;
                                  }
                                  public static int[] sort(int a[],int l)
                                 {
                                             int j=1,temp;
                                             while(j<=l-1)
                                             {
                                                       temp = a[j];
                                                        int k=j-1;
                                                        while(k>=0 && temp<a[k])
                                                       {
                                                                 a[k+1]= a[k];
                                                                 k--;
                                                       }
                                                      a[k+1]=temp;
                                                      j++;
                                             } 
                                 return a;
                          }
                    public static boolean compare(int a[])
                  { 
                           int length=a.length;
                           int diff[] = new int[length-1];
                           boolean flag=true;
                           for(int i=0;i<diff.length;i++)
                          {
                                   diff[i]=a[i]-a[i+1];
                                   if(diff[i]==0)
                                   {
                                              flag=false;
                                              break;
                                   }
                                   else
                                   {
                                            flag=true;
                                   }
                           }
                           return flag;
                      }
                      public static void main(String[] args)         throws IOException 
                     {
                       BufferedReader br =new BufferedReader(new InputStreamReader(System.in));
                       String str = null;
                       boolean result = true;
                       System.out.println("Enter your String.....");
                       str = br.readLine();
                       str = str.toLowerCase();
                       int asc[]=ascii(str);
                       int len = asc.length;
                       int comp[]=sort(asc,len);
                       if(result==compare(comp))
                       {
                           System.out.println("The Given String is Unique");
                       }
                       else
                      {
                              System.out.println("The Given String is not Unique");
                       }
                    }
      

      }

      【讨论】:

      • 不要只是发布代码作为答案,还要包括解释
      【解决方案3】:

      您可以通过检查所有 26 个字母(即 a、b、c、d、..、z)的条件来优化第一个解决方案(indexof == lastindexof)。所以这样就不用遍历所有的字符串了。

      【讨论】:

        【解决方案4】:

        这是我为Kache的答案写的代码(参考破解代码并修改):

        public boolean check() {
            int[] checker = new int[8];
            String inp = "!a~AbBC#~";
            boolean flag = true;
            if (inp.length() > 256)
                flag = false;
            else {
                for(int i=0;i<inp.length();i++) {
                    int x = inp.charAt(i);
                    int index = x/32;
                    x = x%32;
                    if((checker[index] & (1<<x)) > 0) { 
                        flag = false;
                        break;
                    }
                    else
                        checker[index] = checker[index] | 1<<x;
                }
            }
            return flag;
        }
        

        【讨论】:

          【解决方案5】:

          首先检查字符串的大小是否

          【讨论】:

            【解决方案6】:

            选项 2 是三者中最好的 - 散列比搜索更快。

            但是,如果您有足够的内存,还有一种更快的方法。

            利用字符集有限且已枚举的事实,并在检查每个字符时跟踪出现的内容和未出现的内容。

            例如,如果您使用单字节字符,则只有 256 种可能性。当您阅读字符串时,您只需要 256 位来跟踪。如果出现字符 0x00,则翻转第一位。如果出现字符 0x05,则翻转第六位,依此类推。当遇到已经翻转的位时,字符串不是唯一的。

            最坏的情况是 O(min(n, m)),其中 n 是字符串的长度,m 是字符集的大小。

            当然,正如我在另一个人的评论中看到的,如果n > m(即字符串的长度> 字符集的大小),那么根据鸽子洞原理,有一个重复的字符,可以在O(1)中确定时间。

            【讨论】:

            【解决方案7】:

            我不喜欢 1。——这是一个 O(N2) 算法。你的 2. 大致是线性的,但总是遍历整个字符串。你的 3. 是 O(N lg2 N),(可能)有一个相对较高的常数——可能几乎总是比 2 慢。

            然而,我的偏好是,当您尝试将一个字母插入到集合中时,检查它是否已经存在,如果是,您可以立即停止。给定字母的随机分布,这应该只需要平均扫描一半的字符串。

            编辑:两个 cmets 都是正确的,您希望扫描的字符串的确切部分取决于分布和长度——在某些时候,字符串足够长以至于重复是不可避免的,并且(例如)一个没有那个人,机会还是挺高的。事实上,给定一个平坦的随机分布(即集合中的所有字符的可能性相同),这应该与生日悖论非常吻合,这意味着碰撞的机会与可能的字符数的平方根有关字符集。举个例子,如果我们假设基本的 US-ASCII(128 个字符)具有相同的概率,我们将在 14 个字符左右达到 50% 的冲突机会。当然,在真正的字符串中,我们可能会期待它比这更早,因为 ASCII 字符在大多数字符串中的使用频率并不接近相等。

            【讨论】:

            • +1 - 但你的最后一句话是错误的。扫描的平均字符数(假设随机排序/选择的字符)是字母概率分布和平均字符串长度的函数。
            • 跑题了,但是如何获得上标和下标呢?
            • @Kache4:您可以在帖子中使用基本的 html(在本例中为
            • 第三个答案的优点是您可以避免使用额外的数据结构。假设文本中有单词而不是字符,哈希的大小可能会增加很多!
            【解决方案8】:
            public boolean hasUniqChars(String s){
              Hashset h = new Hashset();
              HashSet<Character> h = new HashSet<Character>();
              for (char c : s.toCharArray()) {
              if (!h.add(Character.toUpperCase(c))) // break if already present
                return false;
              }
              return true;
            }
            

            如果您正在执行像 utf-8 这样的字符集并且为了国际化,您应该使用 hashset 技术。

            关于 utf 情况的 Character.toUpperCase 上的 Javadoc: 此方法 (toUpperCase(char) ) 无法处理补充字符。要支持所有 Unicode 字符,包括补充字符,请使用 toUpperCase(int) 方法。

            【讨论】:

              【解决方案9】:

              我建议使用 (2) 的变体 - 使用“字符已看到”标志的数组而不是哈希集。循环遍历字符串时,如果已经看到当前字符,则立即退出。

              如果你有一个可用的位向量类(我忘了 Java 是否提供了),你可以使用它,尽管节省内存不一定会提高速度,并且很容易减慢速度。

              不过,这是 O(n) 最坏的情况,并且根据您的字符串可能具有更好的平均性能 - 您可能会发现大多数字符串在开始附近都有重复。事实上,严格来说,这是 O(1) 最坏的情况,因为长度超过字符集大小的字符串 must 有重复的字符,所以你有一个常量绑定到你需要的字符数签入每个字符串。

              【讨论】:

                【解决方案10】:

                如果使用 int 来存储与 alpabhet 字母的索引对应的位呢?或者可能需要很长时间才能达到 64 个不同的符号。

                long mask;
                // already lower case
                string = string.toLowerCase();
                for (int i = 0; i < string.length(); ++i)
                {
                  int index = 1 << string.charAt(i) - 'a';
                  if (mask & index == index)
                    return false;
                
                  mask |= index;
                }
                return true;
                

                这在平均情况下应该是

                【讨论】:

                  【解决方案11】:

                  您所说的“唯一字母”是指标准的 26 位英文集,还是您允许有趣的 Unicode?如果字符串包含非字母,您期望什么结果?

                  如果您只考虑 26 个可能的字母,并且您想忽略任何非字母或将其视为自动失败,那么最好的算法可能是这个伪代码:

                  create present[26] as an array of booleans.
                  set all elements of present[] to false.
                  loop over characters of your string
                    if character is a letter
                      if corresponding element of present[] is true
                        return false.
                      else
                        set corresponding element of present[] to true.
                      end if
                    else
                      handle non-letters
                    end if
                  end loop
                  

                  剩下的唯一问题是您的数组实际上应该是一个数组(需要 26 次操作归零)还是一个位域(可能需要更多工作来检查/设置,但可以在一次操作中归零)。我认为位域访问几乎可以与数组查找相媲美,如果不是更快的话,所以我希望位域是正确的答案。

                  【讨论】:

                    【解决方案12】:

                    对选项 2 的改进是检查 HashSet add 方法返回的布尔标志。如果对象不在那里,这是真的。不过,要使此方法完全有用,您首先必须将字符串设置为全部大写或小写。

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2012-07-13
                      • 1970-01-01
                      • 1970-01-01
                      • 2017-12-09
                      • 2016-02-01
                      • 1970-01-01
                      • 2011-03-19
                      • 1970-01-01
                      相关资源
                      最近更新 更多