web-dev-qa-db-fra.com

Comment vérifier si deux mots sont des anagrammes

J'ai un programme qui vous montre si deux mots sont des anagrammes les uns des autres. Il y a quelques exemples qui ne fonctionneront pas correctement et j'apprécierais toute aide, bien que si elle n'était pas avancée, ce serait formidable, car je suis programmeur en 1ère année. "maître d'école" et "salle de classe" sont des anagrammes l'un de l'autre. Toutefois, lorsque je change de "salle de classe" en "salle de classe", il est toujours indiqué qu'il s'agit d'anagrammes.

import Java.util.ArrayList;
public class AnagramCheck
{
  public static void main(String args[])
  {
      String phrase1 = "tbeclassroom";
      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;
  }
}
42
Chilli

L'algorithme le plus rapide consisterait à mapper chacun des 26 caractères anglais en un nombre premier unique. Calculez ensuite le produit de la chaîne. Selon le théorème fondamental de l'arithmétique, 2 chaînes sont des anagrammes si et seulement si leurs produits sont identiques.

90
SeriousBusiness

Deux mots sont des anagrammes l'un de l'autre s'ils contiennent le même nombre de caractères et les mêmes caractères. Vous devez uniquement trier les caractères dans l'ordre lexicographique et déterminer si tous les caractères d'une chaîne sont égaux à et dans le même ordre que tous les caractères de l'autre chaîne.

Voici un exemple de code. Recherchez dans Arrays dans l'API pour comprendre ce qui se passe ici.

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);
}
100
Makoto

Si vous triez un tableau, la solution devient O (n log n). mais si vous utilisez un hashmap, c'est O (n). testé et fonctionnel.

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;
48
jb.

Voici une solution simple et rapide O(n) sans tri, ni boucle ni cartes de hachage. Nous incrémentons le nombre de chaque caractère dans le premier tableau et décrémentons le nombre de chaque caractère dans le deuxième tableau. Si le tableau de nombres résultant est plein de zéros, les chaînes sont des anagrammes. Peut être étendu pour inclure d'autres caractères en augmentant la taille du tableau de nombres.

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]));
    }
}
25
Aswin

Beaucoup de personnes ont présenté des solutions, mais je veux juste parler de la complexité algorithmique de certaines des approches courantes:

  • La méthode simple "trier les caractères en utilisant Arrays.sort()" sera O(N log N).

  • Si vous utilisez le tri de base, cela réduit à O(N) avec O(M) espace, où M est le nombre de caractères distincts de l'alphabet. (Cela représente 26 en anglais ... mais en théorie, nous devrions envisager des anagrammes multilingues.)

  • Le "nombre de caractères" à l'aide d'un tableau de comptes est également O(N) ... et plus rapide que le tri à base car il n'est pas nécessaire de reconstruire la chaîne triée. L'utilisation de l'espace sera O(M).

  • Un "décompte des caractères" à l'aide d'un dictionnaire, hashmap, treemap ou équivalent sera plus lent que l'approche tableau, sauf si l'alphabet est énorme.

  • L’approche élégante du «produit des nombres premiers» n’est malheureusement que O(N^2) dans le pire des cas. En effet, pour des mots ou des phrases suffisamment longues, le produit des nombres premiers ne rentre pas dans une long. Cela signifie que vous devez utiliser BigInteger, et N fois, multipliant une BigInteger par une petite constante est O(N^2)

    Pour un grand alphabet hypothétique, le facteur d'échelle va être grand. Dans le pire des cas, l’espace utilisé pour contenir le produit des nombres premiers sous la forme BigInteger est (je pense) O(N*logM).

  • Une approche basée sur hashcode est généralement O(N) si les mots ne sont pas des anagrammes. Si les codes de hachage sont égaux, vous devez toujours effectuer un test d'anagramme approprié. Donc, ce n'est pas une solution complète.

22
Stephen C

O (n) solution sans aucun type de tri et en utilisant une seule carte. 

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

et solution moins générique mais un peu plus rapide. Vous devez placer votre alphabet ici:

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;
}
6
goroncy

Nous parcourons deux chaînes d'égale longueur et suivons les différences entre elles. Peu importe les différences, nous voulons simplement savoir s’ils ont les mêmes personnages ou non. Nous pouvons le faire dans O(n/2) sans post-traitement (ni beaucoup de nombres premiers).

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
  }
}

Oui, ce code dépend du jeu de caractères anglais ASCII anglais composé de minuscules, mais il ne devrait pas être difficile de le modifier dans d’autres langues. Vous pouvez toujours utiliser un mappage [Character, Int] pour suivre les mêmes informations, cela sera simplement plus lent.

4
Jeff Thomas

Beaucoup de réponses compliquées ici. Sur la base de réponse acceptée et le commentaire mentionnant le problème 'ac' - 'bb' en supposant que A = 1 B = 2 C = 3, nous pourrions simplement utiliser le carré de chaque entier représentant un caractère et résoudre le problème:

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;
}
3
chakming
    /*
 * 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;
        //}
        }

}
3
Mokhtar Kalmoush

En utilisant plus de mémoire (une table de hachage d'au plus N/2 éléments), il n'est pas nécessaire de trier les chaînes.

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

Cette fonction est actuellement exécutée dans O(N) ... au lieu de O(NlogN) pour la solution qui trie les chaînes. Si je supposais que vous n'utiliseriez que des caractères alphabétiques, je ne pourrais utiliser qu'un tableau de 26 pouces (de a à z sans accents ni décorations) au lieu du hashmap.

Si nous définissons cela: N = | one | + | two | nous faisons une itération sur N (une fois sur un pour incrémenter les compteurs, et une fois pour les décrémenter sur deux) .

Les autres algorithmes décrits présentent un avantage: ils n'utilisent pas de mémoire supplémentaire, à supposer que Arrays.sort utilise des versions in-situ de QuickSort ou un tri par fusion. Mais puisque nous parlons d'anagrammes, je suppose que nous parlons de langues humaines. Par conséquent, les mots ne devraient pas être assez longs pour donner des problèmes de mémoire.

Je suis un développeur C++ et le code ci-dessous est en C++. Je crois que le moyen le plus rapide et le plus simple de s'y prendre serait le suivant:

Créez un vecteur d’ints de taille 26, avec tous les emplacements initialisés à 0, et placez chaque caractère de la chaîne à la position appropriée dans le vecteur. Rappelez-vous, le vecteur est dans l'ordre alphabétique et donc si la première lettre de la chaîne est z, elle irait dans myvector [26]. Remarque: vous pouvez utiliser des caractères ASCII pour que votre code ressemble à quelque chose comme ceci:

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

Ainsi, l'insertion de tous les éléments prendrait O(n) temps, car vous ne parcourriez la liste qu'une seule fois. Vous pouvez maintenant faire exactement la même chose pour la deuxième chaîne et cela prendrait aussi O(n) temps. Vous pouvez ensuite comparer les deux vecteurs en vérifiant si les compteurs de chaque emplacement sont identiques. S'ils le sont, cela signifie que vous avez le même nombre de CHAQUE caractère dans les deux chaînes et qu'il s'agit donc d'anagrammes. La comparaison des deux vecteurs doit également prendre O(n) temps, car vous ne la parcourez qu'une seule fois. 

Remarque: le code ne fonctionne que pour un seul mot de caractères. Si vous avez des espaces, des chiffres et des symboles, vous pouvez simplement créer un vecteur de taille 96 (caractères ASCII 32-127) et au lieu de dire "a", vous diriez - "étant donné que le caractère espace est le premier du champ. ASCII liste de caractères.

J'espère que ça aide. Si j'ai commis une erreur quelque part, laissez s'il vous plaît un commentaire.

3
muneebahmad

Merci de m'avoir fait remarquer pour commenter, tout en faisant, j'ai découvert qu'il y avait une logique incorrecte. J'ai corrigé la logique et ajouté un commentaire pour chaque morceau de code.

// 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;
}
2
Kuldeep Singh

Jusqu'à présent, toutes les solutions proposées fonctionnent avec des éléments char distincts, et non des points de code. Je voudrais proposer deux solutions pour traiter correctement les paires de substitution aussi (ce sont des caractères de U + 10000 à U + 10FFFF , composé de deux éléments char).

1) Solution O (n logn) sur une ligne utilisant Java 8 CharSequence.codePoints() stream:

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

2) Solution moins élégante O(n) _ ​​(en fait, ce ne sera plus rapide que pour les longues chaînes avec peu de chances d'être des anagrammes)}:

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;
}
1
Alex Salauyou
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;
    }

}
1
Kumar Pallav

IMHO, la solution la plus efficace a été fournie par @Siguza, je l'ai étendue pour couvrir les chaînes avec un espace, par exemple: "William Shakespeare", "Je suis un orthographe faible", "Maître d'école", "La classe"

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();

    }
1
Kaliyug Antagonist

Comment un mathématicien pourrait-il penser le problème avant d’écrire un code:

  1. La relation "sont des anagrammes" entre les chaînes est une relation d'équivalence, donc partitionne l'ensemble de toutes les chaînes en classes d'équivalence.
  2. Supposons que nous ayons une règle pour choisir un représentant (lit d'enfant) dans chaque classe. Il est alors facile de vérifier si deux classes sont identiques en comparant leurs représentants.
  3. Un représentant évident pour un ensemble de chaînes est "the le plus petit élément par ordre lexicographique", qui est facile à calculer à partir de n'importe quel élément en triant. Par exemple, le représentant de la classe d'anagramme contenant 'chapeau' est 'aht'.

Dans votre exemple, "maître d'école" et "salle de classe" sont des anagrammes, car ils sont tous les deux dans la classe d'anagramme avec "acehlmoorsst".

En pseudocode: 

>>> def crib(Word):
...     return sorted(Word)
...
>>> crib("schoolmaster") == crib("theclassroom")
True
1
Colonel Panic

Voici ma solution.Tout d'abord exploser les chaînes dans des tableaux de caractères puis les trier et ensuite comparer si elles sont égales ou non. Je suppose que la complexité temporelle de ce code est O (a + b) .Si a = b on peut dire 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;

    }

Une réponse similaire a peut-être été postée en C++, la voici à nouveau en Java. Notez que la méthode la plus élégante consiste à utiliser un Trie pour stocker les caractères dans un ordre trié. Cependant, cette solution est plus complexe. Une solution consiste à utiliser un hachage pour stocker tous les mots que nous comparons, puis les comparer un par un. Pour les comparer, faites un tableau de caractères avec l’index représentant la valeur ANCII des caractères (en utilisant un normalisateur puisque ie, la valeur ANCII de 'a' est 97) et la valeur représentant le nombre d’occurrences de ce caractère. Cela fonctionnera dans le temps O(n) et utilisera un espace O (m * z) où m est la taille du mot currentWord et z la taille du mot stocké, pour lesquels nous créons un 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;
    }
}
1
ak_2050

La méthode de tri n’est pas la meilleure. Il faut O(n) espace et O(nlogn) temps. Au lieu de cela, créez une carte de hachage de caractères et comptez-les (incrémentez les caractères qui apparaissent dans la première chaîne et décrémentez les caractères qui apparaissent dans la deuxième chaîne). Lorsque le nombre atteint zéro, retirez-le du hachage. Enfin, si deux chaînes sont des anagrammes, la table de hachage sera vide à la fin - sinon, elle ne sera pas vide.

Quelques notes importantes: (1) Ignorer la casse des lettres et (2) Ignorer les espaces.

Voici l'analyse détaillée et l'implémentation en C #: Test si deux chaînes sont des anagrammes

0
Zoran Horvat

Voici une autre approche utilisant HashMap en Java

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

Voici le cas de test

@Test
public void anagramTest() {
    assertTrue(StringUtil.isAnagram("keep" , "PeeK"));
    assertFalse(StringUtil.isAnagram("Hello", "hell"));
    assertTrue(StringUtil.isAnagram("SiLeNt caT", "LisTen cat"));       
}
0
craftsmannadeem
// 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;
    }
0
shiv

La solution la plus simple et complexe O(N) utilise 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;
}
0
chaatna

J'ai vu que personne n'a utilisé l'approche "hashcode" pour trouver les anagrammes. J'ai trouvé que mon approche était légèrement différente de celles discutées ci-dessus, donc j'ai pensé la partager. J'ai écrit le code ci-dessous pour trouver les anagrammes qui fonctionnent en 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;
            }
         }
    }
0
Ripu Daman

Une méthode simple pour déterminer si testString est une anagramme de 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;
}
0
Anshul Abhinav

Désolé, la solution est en C #, mais je pense que les différents éléments utilisés pour arriver à la solution sont assez intuitifs. Léger Tweak requis pour les mots avec un trait d'union, mais pour les mots normaux, il devrait bien fonctionner.

    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;
    }
0
Sai

Une autre solution sans tri.

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;
}
0
jmh
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;
}
0
Amardeep Kumar