如何检查两个单词是不是是字谜

Posted

技术标签:

【中文标题】如何检查两个单词是不是是字谜【英文标题】:How to check if two words are anagrams如何检查两个单词是否是字谜 【发布时间】:2013-02-09 08:06:12 【问题描述】:

我有一个程序可以显示两个单词是否是彼此的字谜。有一些示例无法正常工作,我将不胜感激,尽管如果它不是高级的那会很棒,因为我是一名一年级程序员。 “schoolmaster”和“theclas-s-room”是彼此的字谜,但是当我将“theclas-s-room”更改为“theclafsroom”时,它仍然说它们是字谜,我做错了什么?

import java.util.ArrayList;
public class AnagramCheck 
    public static void main(String args[]) 
        String phrase1 = "tbeclas-s-room";
        phrase1 = (phrase1.toLowerCase()).trim();
        char[] phrase1Arr = phrase1.toCharArray();

        String phrase2 = "schoolmaster";
        phrase2 = (phrase2.toLowerCase()).trim();
        ArrayList<Character> phrase2ArrList = convertStringToArraylist(phrase2);

        if (phrase1.length() != phrase2.length()) 
            System.out.print("There is no anagram present.");
         else 
            boolean isFound = true;
            for (int i = 0; i < phrase1Arr.length; i++) 
                for (int j = 0; j < phrase2ArrList.size(); j++) 
                    if (phrase1Arr[i] == phrase2ArrList.get(j)) 
                        System.out.print("There is a common element.\n");
                        isFound =;
                        phrase2ArrList.remove(j);
                    
                
                if (isFound == false) 
                    System.out.print("There are no anagrams present.");
                    return;
                
            
            System.out.printf("%s is an anagram of %s", phrase1, phrase2);
        
    

    public static ArrayList<Character> convertStringToArraylist(String str) 
        ArrayList<Character> charList = new ArrayList<Character>();
        for (int i = 0; i < str.length(); i++) 
            charList.add(str.charAt(i));
        
        return charList;
    

【问题讨论】:

给数学家的提示:字符串之间的关系“是字谜”是等价关系,因此将所有字符串的集合划分为等价类。写下一条规则,从每个班级中选择一名代表。然后通过比较代表来比较班级。 伙计们 - 如果您要回答这个问题,请,请 1) 检查您的答案是否只是之前 37 个答案的另一个副本,并且 2) 不要只是发布代码。发布您的代码的解释,并说明为什么它与其他答案不同(并且希望比其他答案更好)。 finding if two words are anagrams of each other的可能重复 问题是,为什么代码不能正常工作。所有这些提供代码/算法来解决问题的帖子都是不必要的。接受的答案也没有在代码中找到问题,并且提出了错误的声明(它没有得到参考或证明的支持),但得到了很多支持。这真令人气愤。 【参考方案1】:

如果两个单词包含相同数量的字符和相同的字符,则它们是彼此的字谜。您应该只需要按字典顺序对字符进行排序,并确定一个字符串中的所有字符是否等于并且与另一个字符串中的所有字符的顺序相同

这是一个代码示例。查看 API 中的 Arrays 以了解此处发生的情况。

public boolean isAnagram(String firstWord, String secondWord) 
     char[] word1 = firstWord.replaceAll("[\\s]", "").toCharArray();
     char[] word2 = secondWord.replaceAll("[\\s]", "").toCharArray();
     Arrays.sort(word1);
     Arrays.sort(word2);
     return Arrays.equals(word1, word2);

【讨论】:

您的算法在 O(NlogN) 中运行(N 是两个字符串的长度)有一个使用 O(N/2)~O(N) 内存的 O(N) 算法。看我的回答 我认为你应该把所有的字符都变成小写。 如果两个短语是相互排列的,则忽略空格和大小写,则它们是字谜。 cs.duke.edu/csed/algoprobs/anagram.html 为什么不在排序前先比较每个单词的长度? @EricWoodruff:这可能不是一个 100% 完整的示例;在开始比较长度以及强制所有内容都相同方面有明显的优化。这是为了说明,而不是剪切和粘贴有效代码。【参考方案2】:

最快的算法是将 26 个英文字符中的每一个都映射到一个唯一的素数。然后计算字符串的乘积。根据算术基本定理,两个字符串是字谜当且仅当它们的乘积相同。

【讨论】:

可能很快,但可能不是最快。在这种情况下,为什么不简单地使用两个char[26]?它有一个明显的警告,它只适用于英语。 应该适用于所有角色。也是 O(n),比排序要快。 长句你会很快溢出 64 位,BigDecimals 是 /slow/; int[26] 是一个更安全(更快)的赌注。 我们不能只使用字符值而不是质数,我认为它也可以,不是吗? @user2900314 关键观察是它必须适用于素数。 Anagrams 将具有相同的“素数分解”。【参考方案3】:

如果您对任一数组进行排序,解决方案将变为 O(n log n)。但如果您使用哈希图,则为 O(n)。经过测试和工作。

char[] word1 = "test".toCharArray();
char[] word2 = "tes".toCharArray();

Map<Character, Integer> lettersInWord1 = new HashMap<Character, Integer>();

for (char c : word1) 
    int count = 1;
    if (lettersInWord1.containsKey(c)) 
        count = lettersInWord1.get(c) + 1;
    
    lettersInWord1.put(c, count);


for (char c : word2) 
    int count = -1;
    if (lettersInWord1.containsKey(c)) 
        count = lettersInWord1.get(c) - 1;
    
    lettersInWord1.put(c, count);


for (char c : lettersInWord1.keySet()) 
    if (lettersInWord1.get(c) != 0) 
        return false;
    


return true;

【讨论】:

你需要指定 否则编译不出来。自动装箱可以处理剩下的事情 但是地图是空的,你还没有把字符放进去 我得到 Invalid argument to operation ++/-- 用于行 lettersInWord1.get(c)++; lettersInWord1.get(c)--; "这是未经测试的,可能是一些语法错误"我会更新它以使其语法正确 如果我们在开始时比较字符串的长度以确保它们相等,我们是否可以假设不需要最后一次迭代,只要我们检查第二次的计数时间迭代低于 0?【参考方案4】:

这是一个简单的快速 O(n) 解决方案,无需使用排序或多个循环或哈希映射。我们增加第一个数组中每个字符的计数并减少第二个数组中每个字符的计数。如果结果计数数组全为零,则字符串是字谜。可以通过增加计数数组的大小来扩展以包含其他字符。

class AnagramsFaster

    private static boolean compare(String a, String b)
        char[] aArr = a.toLowerCase().toCharArray(), bArr = b.toLowerCase().toCharArray();
        if (aArr.length != bArr.length)
            return false;
        int[] counts = new int[26]; // An array to hold the number of occurrences of each character
        for (int i = 0; i < aArr.length; i++)
            counts[aArr[i]-97]++;  // Increment the count of the character at i
            counts[bArr[i]-97]--;  // Decrement the count of the character at i
        
        // If the strings are anagrams, the counts array will be full of zeros
        for (int i = 0; i<26; i++)
            if (counts[i] != 0)
                return false;
        return true;
    

    public static void main(String[] args)
        System.out.println(compare(args[0], args[1]));
    

【讨论】:

只是一个快速的小改进,我将添加以下测试,检查两个字符串是否相等(忽略大小写),因为这会使它们自然地变成字谜: if (a.equalsIgnoreCase (b)) 返回真。 完美的O(n)O(1) 解决方案。【参考方案5】:

很多人提出了解决方案,但我只想谈谈一些常见方法的算法复杂性:

简单的“使用Arrays.sort() 对字符进行排序”方法将是O(N log N)

如果您使用基数排序,则使用O(M) 空格减少到O(N),其中M 是字母表中不同字符的数量。 (在英语中是 26 ......但理论上我们应该考虑多语言字谜。)

使用计数数组的“计数字符”也是O(N) ...并且比基数排序更快,因为您不需要重新构建已排序的字符串。空间使用量将为O(M)

除非字母表很大,否则使用字典、哈希图、树图或等效方法“计算字符”会比数组方法慢。

不幸的是,在最坏的情况下,优雅的“素数乘积”方法是O(N^2)。这是因为对于足够长的单词或短语,素数的乘积不适合long。这意味着您需要使用BigInteger,并且将BigInteger 乘以一个小常数N 次是O(N^2)

对于假设的大字母表,比例因子会很大。将素数的乘积保存为BigInteger 的最坏情况下的空间使用是(我认为)O(N*logM)

如果单词不是字谜,则基于hashcode 的方法通常是O(N)。如果哈希码相等,那么您仍然需要进行适当的字谜测试。所以这不是一个完整的解决方案。

【讨论】:

【参考方案6】:

O(n) 解决方案,无需任何排序且仅使用一张地图。

public boolean isAnagram(String leftString, String rightString) 
  if (leftString == null || rightString == null) 
    return false;
   else if (leftString.length() != rightString.length()) 
    return false;
  

  Map<Character, Integer> occurrencesMap = new HashMap<>();

  for(int i = 0; i < leftString.length(); i++)
    char charFromLeft = leftString.charAt(i);
    int nrOfCharsInLeft = occurrencesMap.containsKey(charFromLeft) ? occurrencesMap.get(charFromLeft) : 0;
    occurrencesMap.put(charFromLeft, ++nrOfCharsInLeft);
    char charFromRight = rightString.charAt(i);
    int nrOfCharsInRight = occurrencesMap.containsKey(charFromRight) ? occurrencesMap.get(charFromRight) : 0;
    occurrencesMap.put(charFromRight, --nrOfCharsInRight);
  

  for(int occurrencesNr : occurrencesMap.values())
    if(occurrencesNr != 0)
      return false;
    
  

  return true;

和不太通用的解决方案,但更快一点。你必须把你的字母表放在这里:

public boolean isAnagram(String leftString, String rightString) 
  if (leftString == null || rightString == null) 
    return false;
   else if (leftString.length() != rightString.length()) 
    return false;
  

  char letters[] = 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z';
  Map<Character, Integer> occurrencesMap = new HashMap<>();
  for (char l : letters) 
    occurrencesMap.put(l, 0);
  

  for(int i = 0; i < leftString.length(); i++)
    char charFromLeft = leftString.charAt(i);
    Integer nrOfCharsInLeft = occurrencesMap.get(charFromLeft);
    occurrencesMap.put(charFromLeft, ++nrOfCharsInLeft);
    char charFromRight = rightString.charAt(i);
    Integer nrOfCharsInRight = occurrencesMap.get(charFromRight);
    occurrencesMap.put(charFromRight, --nrOfCharsInRight);
  

  for(Integer occurrencesNr : occurrencesMap.values())
    if(occurrencesNr != 0)
      return false;
    
  

  return true;

【讨论】:

【参考方案7】:

我们正在遍历两个长度相等的字符串并跟踪它们之间的差异。我们不在乎有什么区别,我们只想知道它们是否具有相同的字符。我们可以在 O(n/2) 内完成此操作,无需任何后处理(或大量素数)。

public class TestAnagram 
  public static boolean isAnagram(String first, String second) 
    String positive = first.toLowerCase();
    String negative = second.toLowerCase();

    if (positive.length() != negative.length()) 
      return false;
    

    int[] counts = new int[26];

    int diff = 0;

    for (int i = 0; i < positive.length(); i++) 
      int pos = (int) positive.charAt(i) - 97; // convert the char into an array index
      if (counts[pos] >= 0)  // the other string doesn't have this
        diff++; // an increase in differences
       else  // it does have it
        diff--; // a decrease in differences
      
      counts[pos]++; // track it

      int neg = (int) negative.charAt(i) - 97;
      if (counts[neg] <= 0)  // the other string doesn't have this
        diff++; // an increase in differences
       else  // it does have it
        diff--; // a decrease in differences
      
      counts[neg]--; // track it
    

    return diff == 0;
  

  public static void main(String[] args) 
    System.out.println(isAnagram("zMarry", "zArmry")); // true
    System.out.println(isAnagram("basiparachromatin", "marsipobranchiata")); // true
    System.out.println(isAnagram("hydroxydeoxycorticosterones", "hydroxydesoxycorticosterone")); // true
    System.out.println(isAnagram("hydroxydeoxycorticosterones", "hydroxydesoxycorticosterons")); // false
    System.out.println(isAnagram("zArmcy", "zArmry")); // false
  

是的,此代码依赖于小写字符的 ASCII 英文字符集,但修改为其他语言应该不难。您始终可以使用 Map[Character, Int] 来跟踪相同的信息,它只会更慢。

【讨论】:

【参考方案8】:

通过使用更多内存(最多 N/2 个元素的 HashMap),我们不需要对字符串进行排序。

public static boolean areAnagrams(String one, String two) 
    if (one.length() == two.length()) 
        String s0 = one.toLowerCase();
        String s1 = two.toLowerCase();
        HashMap<Character, Integer> chars = new HashMap<Character, Integer>(one.length());
        Integer count;
        for (char c : s0.toCharArray()) 
            count = chars.get(c);
            count = Integer.valueOf(count != null ? count + 1 : 1);
            chars.put(c, count);
        
        for (char c : s1.toCharArray()) 
            count = chars.get(c);
            if (count == null) 
                return false;
             else 
                count--;
                chars.put(c, count);
            
        
        for (Integer i : chars.values()) 
            if (i != 0) 
                return false;
            
        
        return true;
     else 
        return false;
    

这个函数实际上是在 O(N) ...而不是 O(NlogN) 中运行的,用于对字符串进行排序的解决方案。如果我假设您将只使用字母字符,我只能使用 26 个整数的数组(从 a 到 z,没有重音或装饰)而不是 hashmap。

如果我们定义: N = |一个| + |二| 我们对 N 进行一次迭代(一次超过 1 以增加计数器,一次以减少计数器超过 2)。 然后检查我们在 mose N/2 处迭代的总数。

所描述的其他算法有一个优点:假设 Arrays.sort 使用 QuickSort 或合并排序的就地版本,它们不会使用额外的内存。但由于我们在谈论字谜,我会假设我们在谈论人类语言,因此单词不应该长到足以引起记忆问题。

【讨论】:

【参考方案9】:
    /*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Algorithms;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import javax.swing.JOptionPane;

/**
 *
 * @author Mokhtar
 */
public class Anagrams 

    //Write aprogram to check if two words are anagrams
    public static void main(String[] args) 
        Anagrams an=new Anagrams();
        ArrayList<String> l=new ArrayList<String>();
        String result=JOptionPane.showInputDialog("How many words to test anagrams");
        if(Integer.parseInt(result) >1)
            
            for(int i=0;i<Integer.parseInt(result);i++)
            

                String word=JOptionPane.showInputDialog("Enter word #"+i);
                l.add(word);   
            
            System.out.println(an.isanagrams(l));
        
        else
        
            JOptionPane.showMessageDialog(null, "Can not be tested, \nYou can test two words or more");
        

    

    private static String sortString( String w )
    
        char[] ch = w.toCharArray();
        Arrays.sort(ch);
        return new String(ch);
    

    public boolean isanagrams(ArrayList<String> l)
    
        boolean isanagrams=true; 
        ArrayList<String> anagrams = null;
        HashMap<String, ArrayList<String>> map =  new HashMap<String, ArrayList<String>>();
        for(int i=0;i<l.size();i++)
            
        String word = l.get(i);
        String sortedWord = sortString(word);
            anagrams = map.get( sortedWord );
        if( anagrams == null ) anagrams = new ArrayList<String>();
        anagrams.add(word);
        map.put(sortedWord, anagrams);
            

            for(int h=0;h<l.size();h++)
            
                if(!anagrams.contains(l.get(h)))
                
                    isanagrams=false;
                    break;
                
            

            return isanagrams;
        //
        


【讨论】:

【参考方案10】:

我是一名 C++ 开发人员,下面的代码使用 C++ 编写。我相信最快和最简单的方法是:

创建一个大小为 26 的整数向量,所有槽都初始化为 0,并将字符串的每个字符放在向量中的适当位置。请记住,向量是按字母顺序排列的,因此如果字符串中的第一个字母是 z,它将进入 myvector[26]。注意:这可以使用 ASCII 字符来完成,所以基本上你的代码看起来像这样:

string s = zadg;
for(int i =0; i < s.size(); ++i)
    myvector[s[i] - 'a'] = myvector['s[i] - 'a'] + 1;
 

因此插入所有元素将花费 O(n) 时间,因为您只会遍历列表一次。您现在可以对第二个字符串执行完全相同的操作,这也需要 O(n) 时间。然后,您可以通过检查每个插槽中的计数器是否相同来比较两个向量。如果它们是,这意味着您在两个字符串中都有相同数量的每个字符,因此它们是字谜。两个向量的比较也应该花费 O(n) 时间,因为您只遍历它一次。

注意:该代码仅适用于单个单词的字符。如果你有空格、数字和符号,你可以创建一个大小为 96(ASCII 字符 32-127)的向量,而不是说 - 'a' 你会说 - ' ' 因为空格字符是第一个ASCII 字符列表。

我希望这会有所帮助。如果我在某处犯了错误,请发表评论。

【讨论】:

【参考方案11】:

这里有很多复杂的答案。基于accepted answer 和the comment 提到假设A=65 B=66 C=67 的'ac'-'bb' 问题,我们可以简单地使用代表字符的每个整数的平方来解决问题:

public boolean anagram(String s, String t) 
    if(s.length() != t.length())
        return false;

    int value = 0;
    for(int i = 0; i < s.length(); i++)
        value += ((int)s.charAt(i))^2;
        value -= ((int)t.charAt(i))^2;
    
    return value == 0;

【讨论】:

这不适用于字符串"E" = 5"CD" = 3,4。或者就此而言,任何毕达哥拉斯三元组。 @Codebender 比较前检查长度 另外,字母字符的int值很难造成这种可能的情况 任何 3 个数字的平方和永远不会等于其他 3 个数字的平方和。这只是你的假设还是在某个地方证明了。【参考方案12】:

到目前为止,所有提议的解决方案都适用于单独的 char 项目,而不是代码点。我想提出两个解决方案来正确处理surrogate pairs(这些是字符from U+10000 to U+10FFFF,由两个char 项组成)。

1) 利用 Java 8 CharSequence.codePoints() 流的单行 O(n logn) 解决方案:

static boolean areAnagrams(CharSequence a, CharSequence b) 
    return Arrays.equals(a.codePoints().sorted().toArray(),
                         b.codePoints().sorted().toArray());

2) 不太优雅的 O(n) 解决方案(事实上,它只对不太可能成为字谜的长字符串会更快)

static boolean areAnagrams(CharSequence a, CharSequence b) 
    int len = a.length();
    if (len != b.length())
        return false;

    // collect codepoint occurrences in "a"
    Map<Integer, Integer> ocr = new HashMap<>(64);
    a.codePoints().forEach(c -> ocr.merge(c, 1, Integer::sum));

    // for each codepoint in "b", look for matching occurrence
    for (int i = 0, c = 0; i < len; i += Character.charCount(c)) 
        int cc = ocr.getOrDefault((c = Character.codePointAt(b, i)), 0);
        if (cc == 0)                        
            return false;            
        ocr.put(c, cc - 1);
    
    return true;

【讨论】:

【参考方案13】:

感谢指出评论,评论时发现逻辑不正确。我更正了逻辑并为每段代码添加了注释。

// Time complexity: O(N) where N is number of character in String
// Required space :constant space.
// will work for string that contains ASCII chars

private static boolean isAnagram(String s1, String s2) 

    // if length of both string's are not equal then they are not anagram of each other 
    if(s1.length() != s2.length())return false;

    // array to store the presence of a character with number of occurrences.   
    int []seen = new int[256];

    // initialize the array with zero. Do not need to initialize specifically  since by default element will initialized by 0.
    // Added this is just increase the readability of the code. 
    Arrays.fill(seen, 0);

    // convert each string to lower case if you want to make ABC and aBC as anagram, other wise no need to change the case.  
    s1 = s1.toLowerCase();
    s2 = s2.toLowerCase();

    //  iterate through the first string and count the occurrences of each character
    for(int i =0; i < s1.length(); i++)
        seen[s1.charAt(i)] = seen[s1.charAt(i)] +1;
    

    // iterate through second string and if any char has 0 occurrence then return false, it mean some char in s2 is there that is not present in s1.
    // other wise reduce the occurrences by one every time .
    for(int i =0; i < s2.length(); i++)
        if(seen[s2.charAt(i)] ==0)return false;
        seen[s2.charAt(i)] = seen[s2.charAt(i)]-1;
    

    // now if both string have same occurrence of each character then the seen array must contains all element as zero. if any one has non zero element return false mean there are 
    // some character that either does not appear in one of the string or/and mismatch in occurrences 
    for(int i = 0; i < 256; i++)
        if(seen[i] != 0)return false;
    
    return true;

【讨论】:

【参考方案14】:

恕我直言,@Siguza 提供了最有效的解决方案,我已将其扩展到包含空格的字符串,例如:“William Shakespeare”、“I am a weakish speller”、“School master”、“The class”

public int getAnagramScore(String word, String anagram) 

        if (word == null || anagram == null) 
            throw new NullPointerException("Both, word and anagram, must be non-null");
        

        char[] wordArray = word.trim().toLowerCase().toCharArray();
        char[] anagramArray = anagram.trim().toLowerCase().toCharArray();

        int[] alphabetCountArray = new int[26];

        int reference = 'a';

        for (int i = 0; i < wordArray.length; i++) 
            if (!Character.isWhitespace(wordArray[i])) 
                alphabetCountArray[wordArray[i] - reference]++;
            
        
        for (int i = 0; i < anagramArray.length; i++) 
            if (!Character.isWhitespace(anagramArray[i])) 
                alphabetCountArray[anagramArray[i] - reference]--;
            
        

        for (int i = 0; i < 26; i++)
            if (alphabetCountArray[i] != 0)
                return 0;

        return word.length();

    

【讨论】:

使用 2 个 Hashset 怎么样?【参考方案15】:

类似的答案可能已经在 C++ 中发布,这里又是在 Java 中。请注意,最优雅的方法是使用 Trie 以排序顺序存储字符,但是,这是一个更复杂的解决方案。一种方法是使用哈希集来存储我们正在比较的所有单词,然后逐个比较它们。要比较它们,请使用表示字符的 ANCII 值的索引(使用归一化器,因为 'a' 的 ANCII 值是 97)和表示该字符的出现次数的值来创建一个字符数组。这将在 O(n) 时间内运行并使用 O(m*z) 空间,其中 m 是 currentWord 的大小,z 是 storedWord 的大小,我们为此创建了一个 Char[]。

public static boolean makeAnagram(String currentWord, String storedWord)
    if(currentWord.length() != storedWord.length()) return false;//words must be same length
    Integer[] currentWordChars = new Integer[totalAlphabets];
    Integer[] storedWordChars = new Integer[totalAlphabets];
    //create a temp Arrays to compare the words
    storeWordCharacterInArray(currentWordChars, currentWord);
    storeWordCharacterInArray(storedWordChars, storedWord);
    for(int i = 0; i < totalAlphabets; i++)
        //compare the new word to the current charList to see if anagram is possible
        if(currentWordChars[i] != storedWordChars[i]) return false;
    
    return true;//and store this word in the HashSet of word in the Heap

//for each word store its characters
public static void storeWordCharacterInArray(Integer[] characterList, String word)
    char[] charCheck = word.toCharArray();
    for(char c: charCheck)
        Character cc = c;
        int index = cc.charValue()-indexNormalizer;
        characterList[index] += 1;
    

【讨论】:

【参考方案16】:

数学家在编写任何代码之前会如何思考问题

    字符串之间的“are anagrams”关系是等价关系,因此将所有字符串的集合划分为等价类。 假设我们有一个规则可以从每个班级中选择一个代表(婴儿床),那么通过比较两个班级的代表很容易测试两个班级是否相同。 一组字符串的一个明显代表是“按字典顺序排列的最小元素”,它很容易通过排序从任何元素中计算出来。例如,包含“hat”的字谜类的代表是“aht”。

在您的示例中,“schoolmaster”和“theclas-s-room”是字谜,因为它们都属于带有婴儿床“acehlmoorsst”的字谜类。

在伪代码中:

>>> def crib(word):
...     return sorted(word)
...
>>> crib("schoolmaster") == crib("theclas-s-room")
True

【讨论】:

【参考方案17】:
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
 * Check if Anagram by Prime Number Logic
 * @author Pallav
 *
 */
public class Anagram 
    public static void main(String args[]) 
        System.out.println(isAnagram(args[0].toUpperCase(),
                args[1].toUpperCase()));
    
/**
 * 
 * @param word : The String 1
 * @param anagram_word : The String 2 with which Anagram to be verified
 * @return true or false based on Anagram
 */
    public static Boolean isAnagram(String word, String anagram_word) 
        //If length is different return false
        if (word.length() != anagram_word.length()) 
            return false;
        
        char[] words_char = word.toCharArray();//Get the Char Array of First String
        char[] anagram_word_char = anagram_word.toCharArray();//Get the Char Array of Second String
        int words_char_num = 1;//Initialize Multiplication Factor to 1
        int anagram_word_num = 1;//Initialize Multiplication Factor to 1 for String 2
        Map<Character, Integer> wordPrimeMap = wordPrimeMap();//Get the Prime numbers Mapped to each alphabets in English
        for (int i = 0; i < words_char.length; i++) 
            words_char_num *= wordPrimeMap.get(words_char[i]);//get Multiplication value for String 1
        
        for (int i = 0; i < anagram_word_char.length; i++) 
            anagram_word_num *= wordPrimeMap.get(anagram_word_char[i]);//get Multiplication value for String 2
        

        return anagram_word_num == words_char_num;
    
/**
 * Get the Prime numbers Mapped to each alphabets in English
 * @return
 */
    public static Map<Character, Integer> wordPrimeMap() 
        List<Integer> primes = primes(26);
        int k = 65;
        Map<Character, Integer> map = new TreeMap<Character, Integer>();
        for (int i = 0; i < primes.size(); i++) 
            Character character = (char) k;
            map.put(character, primes.get(i));
            k++;
        
        // System.out.println(map);
        return map;
    
/**
 * get first N prime Numbers where Number is greater than 2
 * @param N : Number of Prime Numbers
 * @return
 */
    public static List<Integer> primes(Integer N) 
        List<Integer> primes = new ArrayList<Integer>();
        primes.add(2);
        primes.add(3);

        int n = 5;
        int k = 0;
        do 
            boolean is_prime = true;
            for (int i = 2; i <= Math.sqrt(n); i++) 
                if (n % i == 0) 
                    is_prime = false;
                    break;
                
            

            if (is_prime == true) 
                primes.add(n);

            
            n++;
            // System.out.println(k);
         while (primes.size() < N);

        // 

        return primes;
    


【讨论】:

【参考方案18】:

这是我的解决方案。首先将字符串分解为 char 数组,然后对它们进行排序,然后比较它们是否相等。我猜这段代码的时间复杂度是 O(a+b)。如果 a=b 我们可以说 O(2A)

public boolean isAnagram(String s1, String s2) 

        StringBuilder sb1 = new StringBuilder();
        StringBuilder sb2 = new StringBuilder();
        if (s1.length() != s2.length())
            return false;

        char arr1[] = s1.toCharArray();
        char arr2[] = s2.toCharArray();
        Arrays.sort(arr1);
        Arrays.sort(arr2);



        for (char c : arr1) 
            sb1.append(c);
        

        for (char c : arr2) 
            sb2.append(c);
        

        System.out.println(sb1.toString());
        System.out.println(sb2.toString());

        if (sb1.toString().equals(sb2.toString()))
            return true;
        else
            return false;

    

【讨论】:

Arrays.sort 的开销是 O(nlogn) > O(n)【参考方案19】:
// When this method returns 0 means strings are Anagram, else Not.

public static int isAnagram(String str1, String str2) 
        int value = 0;
        if (str1.length() == str2.length()) 
            for (int i = 0; i < str1.length(); i++) 
                value = value + str1.charAt(i);
                value = value - str2.charAt(i);
            

         else 
            value = -1;
        
        return value;
    

【讨论】:

【参考方案20】:

排序方法不是最好的方法。它需要 O(n) 空间和 O(nlogn) 时间。相反,制作字符的哈希映射并计算它们(增加第一个字符串中出现的字符并减少出现在第二个字符串中的字符)。当某个计数达到零时,将其从哈希中删除。最后,如果两个字符串是字谜,那么哈希表最后会是空的——否则就不会是空的。

几个重要说明:(1)忽略字母大小写和(2)忽略空格。

这里是C#的详细分析和实现:Testing If Two Strings are Anagrams

【讨论】:

对于每个放入 hashmap ,hashcode 和 equals 都被调用...这对大字符串真的很好...我认为在这种情况下排序会比这快得多 使用散列集的解决方案需要 O(n) 时间和 O(m) 额外空间,其中 m 是两个字符串中不同字符的总数。基本上,如果字符串是纯文本,m 不应超过 100 左右。这意味着哈希集解决方案将渐近执行时间从 O(nlogn) 减少到 O(n),并将添加的空间从 O(n) 减少到实际上 O(1)。这应该会对大字符串产生重大影响。【参考方案21】:

其他一些没有排序的解决方案。

public static boolean isAnagram(String s1, String s2)
    //case insensitive anagram

    StringBuffer sb = new StringBuffer(s2.toLowerCase());
    for (char c: s1.toLowerCase().toCharArray())
        if (Character.isLetter(c))

            int index = sb.indexOf(String.valueOf(c));
            if (index == -1)
                //char does not exist in other s2
                return false;
            
            sb.deleteCharAt(index);
        
    
    for (char c: sb.toString().toCharArray())
        //only allow whitespace as left overs
        if (!Character.isWhitespace(c))
            return false;
        
    
    return true;

【讨论】:

AFAIK 排序两次是 N(Log N)*N(Log N)。该算法需要 NN。此外,并不总是执行第二个循环,这使其平均值介于 N 和 NN 之间。我用 System.nanoTime() 时间差异做了一些粗略的测试,这表明排序比较慢。也许我忽略了一些东西.... 修正:O(N LogN) + O(N Log N) vs O(N) 实际上是 $O(N \log N)$ 与 $O(N^2)$。 也许你能帮我弄清楚为什么会这样以及为什么排序的性能会变慢。我查看了 Skiena 的算法设计手册,同意 NlogN。手册指出 O(f(n)) +O(g(n)) = O(max(f(n),g(n)))。因此,结果变为 O(NlogN) vs N?也许是我,但我不明白为什么这个算法是 N^2,因为我看不到任何嵌套循环,除非它们隐藏在 JAVA 中的某个地方?另外,为什么它的 CPU 时间更短?【参考方案22】:

判断 testString 是否是 baseString 的变位词的简单方法。

private static boolean isAnagram(String baseString, String testString)
    //Assume that there are no empty spaces in either string.

    if(baseString.length() != testString.length())
        System.out.println("The 2 given words cannot be anagram since their lengths are different");
        return false;
    
    else
        if(baseString.length() == testString.length())
            if(baseString.equalsIgnoreCase(testString))
                System.out.println("The 2 given words are anagram since they are identical.");
                return true;
            
            else
                List<Character> list = new ArrayList<>();

                for(Character ch : baseString.toLowerCase().toCharArray())
                    list.add(ch);
                
                System.out.println("List is : "+ list);

                for(Character ch : testString.toLowerCase().toCharArray())
                    if(list.contains(ch))
                        list.remove(ch);
                    
                

                if(list.isEmpty())
                    System.out.println("The 2 words are anagrams");
                    return true;
                
            
        
    
    return false;

【讨论】:

【参考方案23】:

抱歉,解决方案是用 C# 编写的,但我认为用于得出解决方案的不同元素非常直观。连字符的单词需要稍微调整,但对于普通单词应该可以正常工作。

    internal bool isAnagram(string input1,string input2)
    
        Dictionary<char, int> outChars = AddToDict(input2.ToLower().Replace(" ", ""));
        input1 = input1.ToLower().Replace(" ","");
        foreach(char c in input1)
        
            if (outChars.ContainsKey(c))
            
                if (outChars[c] > 1)
                    outChars[c] -= 1;
                else
                    outChars.Remove(c);
            
        
        return outChars.Count == 0;
    

    private Dictionary<char, int> AddToDict(string input)
    
        Dictionary<char, int> inputChars = new Dictionary<char, int>();
        foreach(char c in input)
        
            if(inputChars.ContainsKey(c))
            
                inputChars[c] += 1;
            
            else
            
                inputChars.Add(c, 1);
                 
        
        return inputChars;
    

【讨论】:

【参考方案24】:

我看到没有人使用“哈希码”方法来找出字谜。我发现我的方法与上面讨论的方法几乎没有什么不同,因此想到了分享它。我编写了以下代码来查找在 O(n) 中有效的字谜。

/**
 * This class performs the logic of finding anagrams
 * @author ripudam
 *
 */
public class AnagramTest 

    public static boolean isAnagram(final String word1, final String word2) 

            if (word1 == null || word2 == null || word1.length() != word2.length()) 
                 return false;
            

            if (word1.equals(word2)) 
                return true;
            

            final AnagramWrapper word1Obj = new AnagramWrapper(word1);
            final AnagramWrapper word2Obj = new AnagramWrapper(word2);

            if (word1Obj.equals(word2Obj)) 
                return true;
            

            return false;
        

        /*
         * Inner class to wrap the string received for anagram check to find the
         * hash
         */
        static class AnagramWrapper 
            String word;

            public AnagramWrapper(final String word) 
                this.word = word;
            

            @Override
            public boolean equals(final Object obj) 

                return hashCode() == obj.hashCode();
            

            @Override
            public int hashCode() 
                final char[] array = word.toCharArray();
                int hashcode = 0;
                for (final char c : array) 
                    hashcode = hashcode + (c * c);
                
                return hashcode;
            
         
    

【讨论】:

“ac”是“bb”的变位词吗?还有,有多少个哈希码,有多少个字符组合? 我明白你的意思。我做了一点小小的修改。它在运行时为任何 unicode 字符串组合生成哈希码。 虽然equals 的重载确实实现了等价关系,但它会抛出null == obj 而不是返回false。更糟糕的是,它没有实现等式 wrt anagrams。将字符串放在一个只有 10 个数值为 1 到 10 的字符的字母表上(以促进“乘法散列”)。让你的哈希码是一个十进制数字,并考虑两个字符的字符串,只有:有多少等价类?一旦你的类多于代码,再多聪明的编码都无法挽救你的一天。 这种方法只能快速进行“非字谜”测试。如果两个词/词组有相同的hashcode,你仍然需要做一个更长的测试,看看它们是否是算法。 @ripudam - 如果您有兴趣,可以为自己生成一个示例。从一个单词开始(比如“anagramatic”)并遍历所有其他相同长度的字母字符串......直到找到一个具有相同哈希码的字符串。只需几十亿次迭代就足以找到一个。一旦你找到了,那个字符串 不会 是“anagramatic”的字谜。但是为什么要打扰...【参考方案25】:

这是在 Java 中使用 HashMap 的另一种方法

public static boolean isAnagram(String first, String second) 
    if (first == null || second == null) 
        return false;
    
    if (first.length() != second.length()) 
        return false;
    
    return doCheckAnagramUsingHashMap(first.toLowerCase(), second.toLowerCase());


private static boolean doCheckAnagramUsingHashMap(final String first, final String second) 
    Map<Character, Integer> counter = populateMap(first, second);
    return validateMap(counter);


private static boolean validateMap(Map<Character, Integer> counter) 
    for (int val : counter.values()) 
        if (val != 0) 
            return false;
        
    
    return true;

这是测试用例

@Test
public void anagramTest() 
    assertTrue(StringUtil.isAnagram("keep" , "PeeK"));
    assertFalse(StringUtil.isAnagram("Hello", "hell"));
    assertTrue(StringUtil.isAnagram("SiLeNt caT", "LisTen cat"));       

【讨论】:

【参考方案26】:
private static boolean checkAnagram(String s1, String s2) 
   if (s1 == null || s2 == null) 
       return false;
    else if (s1.length() != s2.length()) 
       return false;
   
   char[] a1 = s1.toCharArray();
   char[] a2 = s2.toCharArray();
   int length = s2.length();
   int s1Count = 0;
   int s2Count = 0;
   for (int i = 0; i < length; i++) 
       s1Count+=a1[i];
       s2Count+=a2[i];
   
   return s2Count == s1Count ? true : false;

【讨论】:

【参考方案27】:

复杂度为 O(N) 的最简单解决方案是使用 Map。

public static Boolean checkAnagram(String string1, String string2) 
    Boolean anagram = true;

    Map<Character, Integer> map1 = new HashMap<>();
    Map<Character, Integer> map2 = new HashMap<>();


    char[] chars1 = string1.toCharArray();
    char[] chars2 = string2.toCharArray();

    for(int i=0; i<chars1.length; i++) 
        if(map1.get(chars1[i]) == null) 
            map1.put(chars1[i], 1);
         else 
            map1.put(chars1[i], map1.get(chars1[i])+1);
        

        if(map2.get(chars2[i]) == null) 
            map2.put(chars2[i], 1);
         else 
            map2.put(chars2[i], map2.get(chars2[i])+1);
        
    

    Set<Map.Entry<Character, Integer>> entrySet1 = map1.entrySet();
    Set<Map.Entry<Character, Integer>> entrySet2 = map2.entrySet();
    for(Map.Entry<Character, Integer> entry:entrySet1) 

        if(entry.getValue() != map2.get(entry.getKey())) 
            anagram = false;
            break;
        
    

    return anagram;

【讨论】:

【参考方案28】:

让我们做一个问题:给定两个字符串 s 和 t,编写一个函数来确定 t 是否是 s 的字谜。

例如, s = "字谜",t = "nagaram",返回真。 s = "老鼠", t = "汽车", 返回 false。

方法一(使用HashMap):

public class Method1 

    public static void main(String[] args) 
        String a = "protijayi";
        String b = "jayiproti";
        System.out.println(isAnagram(a, b ));// output => true

    

    private static boolean isAnagram(String a, String b) 
        Map<Character ,Integer> map = new HashMap<>();
        for( char c : a.toCharArray()) 
            map.put(c,    map.getOrDefault(c, 0 ) + 1 );
        
        for(char c : b.toCharArray()) 
            int count = map.getOrDefault(c, 0);
            if(count  == 0 ) return false ; 
            else map.put(c, count - 1 ) ; 
        
        
        return true;
    


方法二:

public class Method2 
public static void main(String[] args) 
    String a = "protijayi";
    String b = "jayiproti";

    
    System.out.println(isAnagram(a, b));// output=> true


private static boolean isAnagram(String a, String b) 
   
    
    int[] alphabet = new int[26];
    for(int i = 0 ; i < a.length() ;i++) 
         alphabet[a.charAt(i) - 'a']++ ;
    
    for (int i = 0; i < b.length(); i++) 
         alphabet[b.charAt(i) - 'a']-- ;
    
    
    for(  int w :  alphabet ) 
         if(w != 0 ) return false;
    
    return true;
    


方法三:

public class Method3 
public static void main(String[] args) 
    String a = "protijayi";
    String b = "jayiproti";
    
    
    System.out.println(isAnagram(a, b ));// output => true


private static boolean isAnagram(String a, String b) 
    char[] ca = a.toCharArray() ;
    char[] cb = b.toCharArray();
    Arrays.sort(   ca     );
    
    Arrays.sort(   cb        );
    return Arrays.equals(ca , cb );


方法四:

public class AnagramsOrNot 
    public static void main(String[] args) 
        String a = "Protijayi";
        String b = "jayiProti";
        isAnagram(a, b);
    

    private static void isAnagram(String a, String b) 
        Map<Integer, Integer> map = new LinkedHashMap<>();

        a.codePoints().forEach(code -> map.put(code, map.getOrDefault(code, 0) + 1));
        System.out.println(map);
        b.codePoints().forEach(code -> map.put(code, map.getOrDefault(code, 0) - 1));
        System.out.println(map);
        if (map.values().contains(0)) 
            System.out.println("Anagrams");
         else 
            System.out.println("Not Anagrams");
        
    

在 Python 中:

def areAnagram(a, b):
    if len(a) != len(b): return False
    count1 = [0] * 256
    count2 = [0] * 256
    for i in a:count1[ord(i)] += 1
    for i in b:count2[ord(i)] += 1

    for i in range(256):
        if(count1[i] != count2[i]):return False    

    return True


str1 = "Giniiii"
str2 = "Protijayi"
print(areAnagram(str1, str2))

让我们来看看另一个著名的面试问题:从给定的字符串中对 Anagrams 进行分组:

public class GroupAnagrams 
    public static void main(String[] args) 
        String a = "Gini Gina Protijayi iGin aGin jayiProti Soudipta";
        Map<String, List<String>> map = Arrays.stream(a.split(" ")).collect(Collectors.groupingBy(GroupAnagrams::sortedString));
        System.out.println("MAP => " + map);
        map.forEach((k,v) -> System.out.println(k +" and the anagrams are =>" + v ));
        /*
         Look at the Map output:
        MAP => Giin=[Gini, iGin], Paiijorty=[Protijayi, jayiProti], Sadioptu=[Soudipta], Gain=[Gina, aGin]
        As we can see, there are multiple Lists. Hence, we have to use a flatMap(List::stream)
        Now, Look at the output:
        Paiijorty and the anagrams are =>[Protijayi, jayiProti]
       
        Now, look at this output:
        Sadioptu and the anagrams are =>[Soudipta]
        List contains only word. No anagrams.
        That means we have to work with map.values(). List contains all the anagrams.
        
                     
        */
        String stringFromMapHavingListofLists = map.values().stream().flatMap(List::stream).collect(Collectors.joining(" "));
        System.out.println(stringFromMapHavingListofLists);
    

    public static String sortedString(String a) 
        String sortedString = a.chars().sorted()
                .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString();

        return sortedString;

    
    
    /*
     * The output : Gini iGin Protijayi jayiProti Soudipta Gina aGin
     * All the anagrams are side by side.
     */

现在在 Python 中对 Anagrams 进行分组再次变得容易。我们必须: 对列表进行排序。然后,创建字典。现在字典会告诉我们这些字谜在哪里(字典索引)。那么字典的值就是字谜的实际索引。

def groupAnagrams(words):
 
    # sort each word in the list
    A = [''.join(sorted(word)) for word in words]
    dict = 
    for indexofsamewords, names in enumerate(A):
     dict.setdefault(names, []).append(indexofsamewords)
    print(dict)
    #'AOOPR': [0, 2, 5, 11, 13], 'ABTU': [1, 3, 4], 'Sorry': [6], 'adnopr': [7], 'Sadioptu': [8, 16], ' KPaaehiklry': [9], 'Taeggllnouy': [10], 'Leov': [12], 'Paiijorty': [14, 18], 'Paaaikpr': [15], 'Saaaabhmryz': [17], ' CNaachlortttu': [19], 'Saaaaborvz': [20]
 
    for index in dict.values():
     print([words[i] for i in index])
 

if __name__ == '__main__':
 
    # list of words
    words = ["ROOPA","TABU","OOPAR","BUTA","BUAT" , "PAROO","Soudipta",
        "Kheyali Park", "Tollygaunge", "AROOP","Love","AOORP", "Protijayi","Paikpara","dipSouta","Shyambazaar",
        "jayiProti", "North Calcutta", "Sovabazaar"]
 
    groupAnagrams(words)
 

输出:

['ROOPA', 'OOPAR', 'PAROO', 'AROOP', 'AOORP']
['TABU', 'BUTA', 'BUAT']
['Soudipta', 'dipSouta']
['Kheyali Park']
['Tollygaunge']
['Love']
['Protijayi', 'jayiProti']
['Paikpara']
['Shyambazaar']
['North Calcutta']
['Sovabazaar']

另一个重要的字谜问题:找出最大的字谜。次数。 在示例中,ROOPA 是出现次数最多的单词。 因此,['ROOPA' 'OOPAR' 'PAROO' 'AROOP' 'AOORP'] 将是最终输出。

from sqlite3 import collections
from statistics import mode, mean

import numpy as np


# list of words
words = ["ROOPA","TABU","OOPAR","BUTA","BUAT" , "PAROO","Soudipta",
        "Kheyali Park", "Tollygaunge", "AROOP","Love","AOORP",
         "Protijayi","Paikpara","dipSouta","Shyambazaar",
        "jayiProti", "North Calcutta", "Sovabazaar"]

print(".....Method 1....... ")

sortedwords = [''.join(sorted(word)) for word in words]
print(sortedwords)
print("...........")
LongestAnagram = np.array(words)[np.array(sortedwords) == mode(sortedwords)]
# Longest anagram 
print("Longest anagram by Method 1:")
print(LongestAnagram)

print(".....................................................")  

print(".....Method 2....... ")  

A = [''.join(sorted(word)) for word in words]

dict = 

for indexofsamewords,samewords in  enumerate(A):
    dict.setdefault(samewords,[]).append(samewords)
#print(dict)  
#'AOOPR': ['AOOPR', 'AOOPR', 'AOOPR', 'AOOPR', 'AOOPR'], 'ABTU': ['ABTU', 'ABTU', 'ABTU'], 'Sadioptu': ['Sadioptu', 'Sadioptu'], ' KPaaehiklry': [' KPaaehiklry'], 'Taeggllnouy': ['Taeggllnouy'], 'Leov': ['Leov'], 'Paiijorty': ['Paiijorty', 'Paiijorty'], 'Paaaikpr': ['Paaaikpr'], 'Saaaabhmryz': ['Saaaabhmryz'], ' CNaachlortttu': [' CNaachlortttu'], 'Saaaaborvz': ['Saaaaborvz']
     
aa =  max(dict.items() , key = lambda x : len(x[1]))
print("aa => " , aa)    
word, anagrams = aa 
print("Longest anagram by Method 2:")
print(" ".join(anagrams))    

输出:

.....Method 1....... 
['AOOPR', 'ABTU', 'AOOPR', 'ABTU', 'ABTU', 'AOOPR', 'Sadioptu', ' KPaaehiklry', 'Taeggllnouy', 'AOOPR', 'Leov', 'AOOPR', 'Paiijorty', 'Paaaikpr', 'Sadioptu', 'Saaaabhmryz', 'Paiijorty', ' CNaachlortttu', 'Saaaaborvz']
...........
Longest anagram by Method 1:
['ROOPA' 'OOPAR' 'PAROO' 'AROOP' 'AOORP']
.....................................................
.....Method 2....... 
aa =>  ('AOOPR', ['AOOPR', 'AOOPR', 'AOOPR', 'AOOPR', 'AOOPR'])
Longest anagram by Method 2:
AOOPR AOOPR AOOPR AOOPR AOOPR






















     

【讨论】:

【参考方案29】:

这可能是简单的函数调用

函数式代码和命令式代码风格的混合

static boolean isAnagram(String a, String b) 
    
    String sortedA = "";
    Object[] aArr = a.toLowerCase().chars().sorted().mapToObj(i -> (char) i).toArray();
    for (Object o: aArr) 
        sortedA = sortedA.concat(o.toString());
    
    
    
    String sortedB = "";
    Object[] bArr = b.toLowerCase().chars().sorted().mapToObj(i -> (char) i).toArray();
    for (Object o: bArr) 
        sortedB = sortedB.concat(o.toString());
    
    
    if(sortedA.equals(sortedB))    
        return true;
    else
        return false;    

【讨论】:

【参考方案30】:

我能想到 3 个解决方案:

    使用排序
# O(NlogN) + O(MlogM) time, O(1) space 
def solve_by_sort(word1, word2):
    return sorted(word1) == sorted(word2)
    使用字母频率计数
# O(N+M) time, O(N+M) space
def solve_by_letter_frequency(word1, word2):
    from collections import Counter
    return Counter(word1) == Counter(word2)
    使用素数分解的概念。 (为每个字母分配质数)
import operator
from functools import reduce

# O(N) time, O(1) space - prime factorization
def solve_by_prime_number_hash(word1, word2):
    return get_prime_number_hash(word1) == get_prime_number_hash(word2)

def get_prime_number_hash(word):
    letter_code = 'a': 2, 'b': 3, 'c': 5, 'd': 7, 'e': 11, 'f': 13, 'g': 17, 'h': 19, 'i': 23, 'j': 29, 'k': 31,'l': 37, 'm': 41, 'n': 43,'o': 47, 'p': 53, 'q': 59, 'r': 61, 's': 67, 't': 71, 'u': 73, 'v': 79, 'w': 83, 'x': 89, 'y': 97,'z': 101
    return 0 if not word else reduce(operator.mul, [letter_code[letter] for letter in word])

我已经在我的媒体story 中对这些进行了更详细的分析。

【讨论】:

以上是关于如何检查两个单词是不是是字谜的主要内容,如果未能解决你的问题,请参考以下文章

使用基本Java检查两个字符串是不是是彼此的字谜[重复]

Python - 比较 2 个单词并检查它们是不是是字谜

确定两个单词是不是是字谜

如何实现我的字谜和回文函数来检查用户输入的单词?

查找给定单词的字谜

使用 C++ 检查两个字符串是不是是字谜