检查字符串是否包含唯一字符的最简单方法是什么?

时间:2010-03-16 04:45:34

标签: java algorithm string

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

  1. 对于字符串中的每个字符,请检查indexOf(char) == lastIndexOf(char)
  2. 将所有字符添加到HashSet并检查set size == string length。
  3. 将字符串转换为char数组,按字母顺序排序,循环遍历数组元素并检查c[i] == c[i+1]
  4. 目前我最喜欢#2,似乎是最简单的方式。还有其他有趣的解决方案吗?

12 个答案:

答案 0 :(得分:12)

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

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

编辑:两个注释都是正确的,您希望扫描的字符串的哪个部分将取决于分布和长度 - 在某些时候字符串足够长,重复是不可避免的,并且(例如)一个性格不足,机会仍然很高。事实上,给定一个平坦的随机分布(即,集合中的所有字符同样可能),这应该与生日悖论密切相关,这意味着碰撞的可能性与可能的字符数量的平方根有关。字符集。例如,如果我们假设基本的US-ASCII(128个字符)具有相同的概率,那么我们将在大约14个字符处达到50%的碰撞几率。当然,在真正的字符串中,我们可能会比它更早,因为在大多数字符串中,ASCII字符不会与频率接近相等的地方一起使用。

答案 1 :(得分:5)

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

但是,如果你有足够的内存,那么有一种更快的方法。

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

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

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

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

答案 2 :(得分:3)

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

有趣的是,Apache Commons有一个CaseInsensitiveMapsrc),它通过上部套管工作然后降低密钥。您可能知道,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;
}

答案 3 :(得分:3)

“独特字母”的意思是仅仅标准的英文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个操作为零),还是一个位域(可能需要更多的工作来检查/设置,但可以在一次操作中归零)。我认为位域访问与数组查找非常相似,如果不是更快,那么我希望bitfield是正确的答案。

答案 4 :(得分:1)

选项2的改进是检查HashSet add方法返回的布尔标志。如果物体不在那里,那就是真的。虽然,为了使这个方法有用,你首先必须将字符串设置为全部大写或小写。

答案 5 :(得分:1)

使用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;

这应该是&lt; O(n)平均情况下,O(n)最差。但是我不确定在Java中有多少高性能的按位操作。

答案 6 :(得分:1)

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)方法。

答案 7 :(得分:1)

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

如果你有一个bitvector类可用(我忘记了Java是否提供了一个),你可以使用它,虽然节省内存不一定会带来任何速度提升,并且很容易减慢速度。

这是O(n)最糟糕的情况,并且可能会有更好的平均性能,具体取决于你的字符串 - 你可能会发现大多数都在一开始就有重复。事实上,严格地说,无论如何,这是O(1)最坏的情况,因为字符串长度超过字符集的字符串必须重复字符,所以你有一个常量绑定到你需要的字符数检查每个字符串。

答案 8 :(得分:1)

首先检查字符串的大小是否为&lt; = 26。如果不是,String有重复。返回 否则尝试添加到HashSet中,如果失败,则字符串重复返回。 如果HashSet的大小=字符串字符串的大小具有唯一字符。 如果我们不允许使用任何其他数据结构和字符串的内部方法,并且仍然必须在O(n)中执行,则循环通过String.if i!= myLastIndexof(i),返回重复项存在。

答案 9 :(得分:0)

这是我为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;
}

答案 10 :(得分:0)

只需检查所有26个字母的条件,即a,b,c,d,..,z,即可优化第一个解决方案(indexof == lastindexof)。所以这种方式你不必遍历所有的字符串。

答案 11 :(得分:-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");
                 }
              }

}