簡體   English   中英

檢查字符串是否由唯一字母組成的最簡單方法?

[英]Easiest way of checking if a string consists of unique letters?

如果一個單詞由唯一字母組成(不區分大小寫),我需要檢查 Java。 由於直接解決方案很無聊,我想出了:

  1. 對於字符串中的每個字符,檢查是否indexOf(char) == lastIndexOf(char)
  2. 將所有字符添加到HashSet並檢查是否設置大小 == 字符串長度。
  3. 將字符串轉換為字符數組,按字母順序排序,遍歷數組元素並檢查是否c[i] == c[i+1]

目前我最喜歡#2,似乎是最簡單的方法。 還有其他有趣的解決方案嗎?

我不喜歡 1。——這是一個 O(N 2 ) 算法。 你的 2. 大致是線性的,但總是遍歷整個字符串。 你的 3. 是 O(N lg 2 N),(可能)一個相對較高的常數——可能幾乎總是比 2 慢。

然而,我的偏好是,當您嘗試將一個字母插入到集合中時,檢查它是否已經存在,如果存在,您可以立即停止。 鑒於字母的隨機分布,這應該平均只需要掃描一半的字符串。

編輯:這兩條評論都是正確的,您希望掃描的字符串的確切部分取決於分布和長度——在某些時候,字符串足夠長,重復是不可避免的,並且(例如)缺少一個字符那個,機會還是相當高的。 事實上,給定一個平坦的隨機分布(即集合中的所有字符的可能性相等),這應該與生日悖論非常吻合,這意味着碰撞的機會與可能的字符數的平方根有關。字符集。 舉個例子,如果我們假設基本的 US-ASCII(128 個字符)的概率相等,我們會在 14 個字符左右時達到 50% 的碰撞幾率。 當然,在真實的字符串中,我們可能會比這更早,因為在大多數字符串中,ASCII 字符的使用頻率並不接近相等。

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

但是,如果您有足夠的內存,還有一種更快的方法。

利用字符集有限且已經枚舉的事實,並在檢查每個字符時跟蹤已出現和未出現的內容。

例如,如果您使用一字節字符,則只有 256 種可能性。 當您閱讀字符串時,您只需要 256 位來跟蹤。 如果出現字符 0x00,則翻轉第一位。 如果出現字符 0x05,則翻轉第六位,依此類推。 當遇到一個已經翻轉的位時,該字符串不是唯一的。

最壞的情況是 O(min(n, m)),其中 n 是字符串的長度,而 m 是字符集的大小。

當然,正如我在另一個人的評論中看到的,如果 n > m(即字符串的長度 > 字符集的大小),那么根據鴿巢原理,存在重復字符,可在 O(1) 時間內確定。

我喜歡 HashSet 的想法。 它在概念上很簡單,並且只有一個通過字符串。 對於簡單的性能改進,請檢查add返回值。 您應該注意的一件事是,這是通過 case-folding 來工作的。 在一個方向。 您可以在 Character 周圍創建一個具有不同 equals 語義的包裝類,以真正不區分大小寫。

有趣的是,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;
}

您所說的“唯一字母”是指標准的 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 次操作歸零)還是位域(可能需要更多的工作來檢查/設置,但可以在單個操作中歸零)。 我認為位域訪問將與數組查找非常相似,如果不是更快,所以我希望位域是正確的答案。

首先檢查字符串的大小是否 <=26。 如果不是,則 String 有重復項。 return 否則嘗試添加到 HashSet 中,如果失敗,則字符串有重復項返回。 如果 HashSet 的大小是 = 字符串字符串的大小具有唯一字符。 如果我們不允許使用任何其他數據結構和字符串的內部方法,並且仍然必須在 O(n) 中執行,則循環遍歷 String.if i!=myLastIndexof(i),返回重復項存在。

選項 2 的改進是檢查 HashSet add 方法返回的布爾標志。 如果對象不存在,則為 true。 但是,要使此方法有用,您首先必須將字符串設置為全部大寫或小寫。

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

這在平均情況下應該 < O(n),在最壞情況下應該是 O(n)。 但我不確定 Java 中有多少高性能的按位運算..

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

我會建議 (2) 的變體 - 使用“已經看到的字符”標志的數組而不是哈希集。 當您遍歷字符串時,如果已經看到當前字符,請立即退出。

如果您有一個可用的 bitvector 類(我忘記 Java 是否提供了一個),您可以使用它,盡管節省內存不一定會導致任何速度提高並且很容易減慢速度。

不過,這是 O(n) 最壞的情況,並且根據您的字符串可能有更好的平均性能 - 您可能會發現大多數在開始附近都有重復。 事實上,嚴格來說,無論如何都是 O(1) 最壞的情況,因為長度超過字符集大小的字符串必須有重復的字符,因此您有一個常量綁定到您需要在每個字符串中檢查的字符數。

這是我為 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;
}

您可以通過檢查所有 26 個字母的條件(即 a、b、c、d、..、z)來優化第一個解決方案(indexof == lastindexof)。 所以這樣你就不必遍歷所有的字符串。

           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");
                 }
              }

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM