2016-07-06 59 views
2

我正在經歷一個排列/字謎問題,並希望以最有效的方式進行檢查。 現在,我正在Java土地上做這件事,因此有一個包括排序在內的所有事物的庫。 檢查兩個字符串是否相互對齊的第一種方法是檢查長度,以某種方式對它們進行排序,然後比較所述字符串的每個索引。代碼如下:謎語檢查的最佳解決方案?

private boolean validAnagram(String str, String pair) { 
if(str.length() != pair.length()){ 
    return false; 
} 

char[] strArr = str.toCharArray(); 
char[] pairArr = pair.toCharArray(); 


Arrays.sort(strArr); 
str = new String(strArr); 

Arrays.sort(pairArr); 
pair = new String(pairArr); 

for(int i = 0; i<str.length(); i++){ 
    if(str.charAt(i) != pair.charAt(i)){ 
     return false; 
    } 
} 
return true; 
} 

另外,我認爲這將更容易檢查基於ASCII值,並避免檢查每個可能的字符。代碼如下:

private boolean validAnagram(String str, String pair) { 
if(str.length() != pair.length()){ 
    return false; 
} 

char[] strArr = str.toCharArray(); 
char[] pairArr = pair.toCharArray(); 



int strValue = 0; 
int pairValue = 0; 

for(int i =0; i < strArr.length; i++){ 
    strValue+= (int) strArr[i]; 
    pairValue+= (int) pairArr[i]; 
} 

if(strValue != pairValue){ 
    return false; 
} 
return true; 
} 

那麼,這是一個更好的解決方案?我對陣列給我的那種瞭解不多,但是當我環顧舊的互聯網時,這是更常見的答案。讓我想知道我是否錯過了一些東西。

+1

相反的轉換'的char []'回'String',然後做'的charAt()',你可以直接比較數組中的字符。 – QBrute

+0

這是令人困惑的。你只需要anagrams或任何排列?檢查一個或另一個的方法是完全不同的。 – fge

+3

我很確定第二個解決方案不起作用。它將返回真'交流'和'bb' –

回答

-1

有幾種方法可以檢查兩個字符串是否是anagrams。 你的問題是,哪一個更好的解決方案。 你的第一個解決方案有排序邏輯。 (nlogn)排序的最壞情況下的複雜度爲 。 你的第二個邏輯是隻使用一個複雜度爲 O(n)的循環。

因此,在這兩者中,只有O(n) 複雜度的第二種解決方案將是比第一種解決方案更好的解決方案。

一個可能的解決方案:

private boolean checkAnagram(String stringOne , String stringTwo){ 
     char[] first = stringOne.toLowerCase().toCharArray(); 
     char[] second = stringTwo.toLowerCase().toCharArray(); 
     // if length of strings is not same 
     if (first.length != second.length) 
      return false; 
     int[] counts = new int[26]; 
     for (int i = 0; i < first.length; i++){ 
      counts[first[i]-97]++; 
      counts[second[i]-97]--; 
     } 
     for (int i = 0; i<26; i++) 
      if (counts[i] != 0) 
       return false; 
     return true; 
    } 

+0

嘿Pratik! 這是我最初的想法。但是,有人指出我的ASCII解決方案存在一個主要問題。基於某種組合可能會得到錯誤的解決方案。如果你給它的字符串是AD和BC,第一個的ascii值是65和68,第二個是66和67的值,它們總和爲133,並且會被視爲相等通過你的算法。「 但是,似乎有解決方法。爲了解決這個問題,似乎並不值得修正邊緣案例。 –

+0

full post here:https://www.reddit.com/r/learnprogramming/comments/4rjg9x/which_is_the_better_anagram_solution/ –

+0

您可以使用另一種使用hashmap的方法。 –

0

最好的解決方案取決於你的目標,代碼大小,內存佔用或至少計算。

一個非常涼溶液,更少的代碼成爲可能,不是最快O(n日誌n)和漂亮存儲器低效在Java中8:

public class Anagram { 
    public static void main(String[] argc) { 
    String str1 = "gody"; 
    String str2 = "dogy"; 

    boolean isAnagram = 
    str1.chars().mapToObj(c -> (char) c).sorted().collect(Collectors.toList()) 
    .equals(str2.chars().mapToObj(c -> (char) c).sorted().collect(Collectors.toList())); 

    System.out.println(isAnagram); 
    } 
} 
+0

該解決方案存在一些故障。根據你的解決方案,你從方法參數中接收到的字符串中分類字符,但是你不會忽略空格和大寫字母,例如:「isAnagram(」William Shakespeare「,」我是一個微弱的拼寫器「)」返回false而不是真實的。 –

0

我嘗試了一些解決方案使用集,並取得每一個運行1000萬次使用您的示例性陣列來測試:

private static String[] input = {"tea", "ate", "eat", "apple", "java", "vaja", "cut", "utc"};

首先,我使用的方法來調用這些algotirhms:

public static void main(String[] args) { 
    long startTime = System.currentTimeMillis(); 
    for (int x = 0; x < 10000000; x++) { 
     Set<String> confirmedAnagrams = new HashSet<>(); 
     for (int i = 0; i < (input.length/2) + 1; i++) { 
      if (!confirmedAnagrams.contains(input[i])) { 
       for (int j = i + 1; j < input.length; j++) { 
         if (isAnagrams1(input[i], input[j])) { 
          confirmedAnagrams.add(input[i]); 
          confirmedAnagrams.add(input[j]); 
         } 
       } 
      } 
     } 
     output = confirmedAnagrams.toArray(new String[confirmedAnagrams.size()]); 
    } 
    long endTime = System.currentTimeMillis(); 
    System.out.println("Total time: " + (endTime - startTime)); 
    System.out.println("Average time: " + ((endTime - startTime)/10000000D)); 
} 

然後我使用基於HashSet字符的算法。我將每個單詞的每個字符添加到HashSet中,並且如果HashSet不是單詞首字母的長度,那將意味着它們不是字形。

我的算法及其運行時間:

算法1:

private static boolean isAnagrams1(String x, String y) { 
    if (x.length() != y.length()) { 
     return false; 
    } else if (x.equals(y)) { 
     return true; 
    } 

    Set<Character> anagramSet = new HashSet<>(); 
    for (int i = 0; i < x.length(); i++) { 
     anagramSet.add(x.charAt(i)); 
     anagramSet.add(y.charAt(i)); 
    } 

    return anagramSet.size() != x.length(); 
} 

這樣做的運行時:

Total time: 6914 
Average time: 6.914E-4 

算法2

private static boolean isAnagrams2(String x, String y) { 
    if (x.length() != y.length()) { 
     return false; 
    } else if (x.equals(y)) { 
     return true; 
    } 

    Set<Character> anagramSet = new HashSet<>(); 
    char[] xAr = x.toCharArray(); 
    char[] yAr = y.toCharArray(); 
    for (int i = 0; i < xAr.length; i++) { 
     anagramSet.add(xAr[i]); 
     anagramSet.add(yAr[i]); 
    } 

    return anagramSet.size() != x.length(); 
} 

擁有的運行時間:

Total time: 8752 
Average time: 8.752E-4 

算法3

對於這個算法,我決定通過發送集,所以我只有一次創建它的每一個週期,每個後清除測試。

private static boolean isAnagrams3(Set<Character> anagramSet, String x, String y) { 
    if (x.length() != y.length()) { 
     return false; 
    } else if (x.equals(y)) { 
     return true; 
    } 

    for (int i = 0; i < x.length(); i++) { 
     anagramSet.add(x.charAt(i)); 
     anagramSet.add(y.charAt(i)); 
    } 

    return anagramSet.size() != x.length(); 
} 

擁有的運行時間:

Total time: 8251 
Average time: 8.251E-4 

算法4

這種算法不是我的,它屬於Pratik Upacharya該回答的問題還有,爲了讓我比較:

private static boolean isAnagrams4(String stringOne, String stringTwo) { 
    char[] first = stringOne.toLowerCase().toCharArray(); 
    char[] second = stringTwo.toLowerCase().toCharArray(); 
    // if length of strings is not same 
    if (first.length != second.length) { 
     return false; 
    } 
    int[] counts = new int[26]; 
    for (int i = 0; i < first.length; i++) { 
     counts[first[i] - 97]++; 
     counts[second[i] - 97]--; 
    } 
    for (int i = 0; i < 26; i++) { 
     if (counts[i] != 0) { 
      return false; 
     } 
    } 
    return true; 
} 

has t他運行時的:

Total time: 5707 
Average time: 5.707E-4 

當然,這些運行時做每個測試運行不同,爲了做正確的測試,一個更大的示例設置是必要的,也許更多的迭代上。

*編輯,正如我在最初的方法犯了一個錯誤,Pratik Upacharya's算法似乎是一個更快的

+0

你的_Algorithm 1_對'isAnagrams1(「good」,「dogg」)返回'true',你需要確保每個字符出現相同的次數。 – Kyriakos

+0

是的,那麼設置的東西不會真的有效。對於那個很抱歉。 – Propagandian

+1

您可以使用'HashMap '來增加/減少計數,這與Pratik對數組的處理方式類似。 – Kyriakos

3

這裏是一個非常簡單的實現。

public boolean isAnagram(String strA, String strB) { 
    // Cleaning the strings (remove white spaces and convert to lowercase) 
    strA = strA.replaceAll("\\s+","").toLowerCase(); 
    strB = strB.replaceAll("\\s+","").toLowerCase(); 

    // Check every char of strA and removes first occurence of it in strB 
    for (int i = 0; i < strA.length(); i++) { 
    if (strB.equals("")) return false; // strB is already empty : not an anagram 
    strB = strB.replaceFirst(Pattern.quote("" + strA.charAt(i)), ""); 
    } 

    // if strB is empty we have an anagram 
    return strB.equals(""); 
} 

最後:

System.out.println(isAnagram("William Shakespeare", "I am a weakish speller")); // true 
0
//here best solution for an anagram 
import java.util.*; 

class Anagram{ 
public static void main(String arg[]){ 

Scanner sc =new Scanner(System.in); 
String str1=sc.nextLine(); 
String str2=sc.nextLine(); 
int i,j; 

boolean Flag=true; 
i=str1.length(); 
j=str2.length(); 


if(i==j){ 
for(int m=0;m<i;m++){ 
    for(int n=0;n<i;n++){ 
     if(str1.charAt(m)==str2.charAt(n)){ 
      Flag=true; 
      break; 
      } 
      else 
      Flag=false; 
    } 
} 
} 
else{ 
Flag=false; 
} 

if(Flag) 
System.out.println("String is Anagram"); 
else 
System.out.println("String is not Anagram"); 
} 
} 
+1

當我接受兩個既不是字謎的字符串也不是「最好」的算法時,也不會將它們排列爲「字符串是字謎」。 – Tom

0

招聘人員問我最近來解決這個問題。 在研究這個問題時,我想出了一個解決方案,它解決了兩種類型的謎題問題。

問題1: 確定一個文本體內是否存在anagram。

問題2: 確定正文中是否存在正式的字謎。 在這種情況下,謎語的大小必須與您將其與 進行比較的文字大小相同。在前一種情況下,這兩種文本的大小不必相同。
一個只需要包含另一個。

我的方法如下:

建立階段: 首先創建一個字謎類。這只是將文本轉換爲一個Map ,其中的關鍵字是字符,值包含輸入字符出現次數 。 我認爲最多這需要O(n)時間複雜度。因爲這最多需要兩張地圖,所以最壞情況下的複雜度爲 將是O(2n)。至少我對漸近符號 說的那種天真的理解。

處理階段: 您只需循環兩個地圖中較小的一個,然後在較大的地圖中查看它。如果它不存在,或者它存在 但是具有不同的出現次數,則它不能使測試成爲一個字謎。

這裏是確定,如果我們有一個字謎或不循環:

boolean looking = true; 
     for (Anagram ele : smaller.values()) { 
      Anagram you = larger.get(ele); 
       if (you == null || you.getCount() != ele.getCount()) { 
        looking = false; 
        break; 
       } 
     } 
     return looking; 

注意,我創建了一個ADT包含正在處理的字符串。他們首先將 轉換爲Map。

下面是代碼片段創建字謎對象:

private void init(String teststring2) { 
     StringBuilder sb = new StringBuilder(teststring2); 
     for (int i = 0; i &lt sb.length(); i++) { 
      Anagram a = new AnagramImpl(sb.charAt(i)); 
      Anagram tmp = map.putIfAbsent(a, a); 
      if (tmp != null) { 
       tmp.updateCount(); 
      } 
     } 
    }