web-dev-qa-db-fra.com

Algorithme d'anagramme en java

J'aimerais faire un algorithme d'anagramme mais Ce code ne fonctionne pas. Où est ma faute? Par exemple, des et sed est une anagramme mais la sortie n'est pas une anagramme En attendant, je dois utiliser la méthode des chaînes. pas de tableau. :)

public static boolean isAnagram(String s1 , String s2)
{
    String delStr="";
    String newStr="";

    for(int i=0;i<s1.length();i++)
    {
        for(int j=0 ; j < s2.length() ; j++)
        {
            if(s1.charAt(i)==s2.charAt(j))
            {
                delStr=s1.substring(i,i+1);
                newStr=s2.replace(delStr,"");
            }
        }           
    }

    if(newStr.equals(""))
        return true;
    else
        return false;
}
7
SemihY

Un moyen plus simple pourrait être de simplement trier les caractères dans les deux chaînes et de comparer s’ils sont égaux:

public static boolean isAnagram(String s1, String s2){

        // Early termination check, if strings are of unequal lengths,
        // then they cannot be anagrams
        if ( s1.length() != s2.length() ) {
            return false;
        }
        s1=s1.toLowerCase();
        s2=s2.toLowerCase();
        char[] c1 = s1.toCharArray();
        char[] c2 = s2.toCharArray();
        Arrays.sort(c1);
        Arrays.sort(c2);
        String sc1 = new String(c1);
        String sc2 = new String(c2);
        return sc1.equals(sc2);
}

Personnellement, je pense que c'est plus lisible que les boucles for imbriquées = p

Cela a O (n log n) complexité d'exécution, où n est la longueur de la chaîne la plus longue.

Edit: ce n'est pas la solution optimale. Voir la réponse de @ aam1r pour connaître l’approche la plus efficace (c’est-à-dire ce que vous devriez réellement dire lors d’une interview).

30
sampson-chen

Cela peut être fait en temps linéaire en utilisant espace constant . Voici un pseudo-code pour vous aider à démarrer:

// Create new hashtable/hashmap to keep track of how many times each character
// is being used
character_map -> new hash map

// Initial check. If lengths are not the same, they can't be anagrams.
if s1.length != s2.length:
    throw exception "Not anagrams"

// Add all characters from s1 to hashmap. Increment the value to keep track of
// number of occurences
foreach character c1 in s1:
    character_map[c1]++

// Iterate through all character in s2 and decrement count of each character.
foreach character c2 in s2:
    character_map[c2]--

// If they are anagrams, each character should be at "0" count at the point.
// If we come across a character that is not, it means that they are not anagrams
foreach key k, value v in character_map:
    if v != 0:
            throw exception "Not anagrams"

Ce code ne trie pas et peut donc être utilisé avec des boucles simples. Le temps d’exécution global est O(n) et l’espace total est O(1) - ce qui en fait la solution la plus rapide. Le nombre d’éléments que vous pouvez avoir dans la carte de hachage est constant (c’est-à-dire que vous savez combien il y a d’éléments dans votre alphabet).

29
Aamir
if(s1.charAt(i)==s2.charAt(j))
        delStr=s1.substring(i,i+1);
        newStr=s2.replace(delStr,"");

Ce code est une belle démonstration de la raison pour laquelle vous devriez toujours avoir curly braces autour de votre if, même s'il n'y a qu'une seule déclaration. Votre 2e affectation est en fait en dehors du if-condition et aura toujours lieu.

Le meilleur moyen de vérifier si deux chaînes sont Anagram consiste à les convertir en un tableau de caractères (String#toCharArray). Ensuite, triez-les en utilisant la méthode Arrays.sort. Et faites la comparaison sur eux.


Mis à jour : -

Si vous voulez simplement utiliser les méthodes String, vous n'avez pas réellement besoin d'une boucle imbriquée ici. Vous pouvez le faire avec un seul.

Voici votre code modifié: -

public static boolean isAnagram(String s1 , String s2){

    if (s1.length() != s2.length()) {
        return false;
    }

    for(int i = 0; i < s2.length(); i++) {

            if( !s1.contains("" + s2.charAt(i))) {
                return false;
            }

            s1 = s1.replaceFirst("" + s2.charAt(i), "");
            s2 = s2.replaceFirst("" + s2.charAt(i), "");
    }
    return true;
}
7
Rohit Jain

Ce qui serait plus efficace est de comparer les chaînes dans un ordre trié.

public static boolean isAnagram(String s1 , String s2) {
    return s1.length() == s2.length() 
        && checkSum(s1) == checkSum(s2)
        && Arrays.equals(lettersSorted(s1), lettersSorted(s2));
}

static long checkSum(String s) {
    long sqrSum = 0;
    for(int i = 0; i < s.length(); s++) {
       char ch = s.charAt(i);
       sqrSum += ch + (1L << ch);
    }
}

static char[] lettersSorted(String s) {
    char[] chars = s.toCharArray();
    Arrays.sort(chars);
    return chars;
}

C'est un algorithme O (N ln N), mais sera O(N) en moyenne si les chaînes ne sont généralement pas des anagrammes.

6
Peter Lawrey

Je ne sais pas ce que vous essayez de faire, mais je suis à peu près sûr que cela ne fonctionnera pas (et cela fonctionne dans O(n^2). Essayez ceci (qui fonctionne dans O(n log n)) à la place:

public static boolean isAnagram(String s1, String s2){
  if (s1.length() != s2.length()) return false;

  char[] c1 = s1.toCharArray();
  char[] c2 = s2.toCharArray();

  Arrays.sort(c1);
  Arrays.sort(c2);

  for(int i = 0; i < c1.length; i++) {
    if(c1[i] != c2[i]) return false;
  }

  return true;
}
6
durron597

La raison pour laquelle cela ne fonctionne pas:

En utilisant "des" et "sed" comme exemple.

Dans la dernière itération pour laquelle il correspond, il évaluera:

if(s1.charAt(i)==s2.charAt(j))
{
    delStr=s1.substring(i,i+1);
    newStr=s2.replace(delStr,"");
}

Qui sera: si ("s" == "s")

Il entrera ensuite dans le bloc if et évaluera

newStr = "sed".replace("s","");

ce qui vous donnera "ed", au lieu d'une chaîne vide.

La morale de l'histoire est que vous remplacez toujours les personnages de s2 moins un, qui ne seront jamais vides.

Utiliser String.replace () est quand même mauvais, car il remplacera toutes les occurrences du caractère par défaut. Avec String.replace (), il serait considéré que "sed" est un anagramme de "seeeeeeed". Vous feriez mieux d'utiliser String.replaceFirst ().

Dans tous les cas, un point de départ consiste à apporter les modifications suivantes:

String newStr = s2;
...
// inside if block
newStr = newStr.replaceFirst( delStr, "" );
2
cmonkey
import Java.util.Scanner;

public class Anagrams {

static boolean isAnagram(String a, String b) {
    a = a.toLowerCase();
    b = b.toLowerCase();
    if (a.length() != b.length()) {
        return false;
    }

    char[] chars = a.toCharArray();
    for (char c : chars) {
        int index = b.indexOf(c);
        if (index != -1) {
            b = b.substring(0, index) + b.substring(index + 1, b.length());
        } else {
            return false;
        }
    }
    return b.isEmpty();
}

public static void main(String[] args) {
    Scanner scan = new Scanner(System.in);
    String a = scan.next();
    String b = scan.next();
    scan.close();
    boolean ret = isAnagram(a, b);
    System.out.println((ret) ? "Anagrams" : "Not Anagrams");

    }
}
1
isurie

Vous trouverez ci-dessous un extrait de code concis qui détermine si deux chaînes sont des anagrammes en une seule itération des deux chaînes, plus une itération finale d'un tableau de 256 éléments. Cette approche évite de trier les caractères dans les chaînes et de convertir en/à partir des tableaux de chaînes/caractères en enregistrant le nombre de caractères dans un tableau de mappage.

static boolean isAnagram(String s1, String s2) {
    if (s1.length() != s2.length()) return false;
    int n = s1.length();
    int[] charMap = new int[256];
    for (int i = 0; i < n; i++) {
        char c1 = s1.charAt(i);
        charMap[c1]++;
        char c2 = s2.charAt(i);
        charMap[c2]--;
    }
    for (int i = 0; i < charMap.length; i++) {
        if (charMap[i] != 0) return false;
    }
    return true;
}

En gros, ce code incrémente et décrémente un emplacement d'index dans un tableau correspondant à un caractère. Si l'un des éléments du tableau est non nul à la fin de l'itération, le nombre d'incréments et de décréments est inégal. Par conséquent, les chaînes contiennent des caractères différents et ne peuvent pas être des anagrammes les unes des autres.

Étant donné que cet algorithme itère une fois les deux chaînes de même taille, le temps d’exécution est O (n). La complexité de l'espace est O(1), car charMap est toujours constant en fonction des exigences du jeu de caractères.

1
broadbear
public boolean checkAnagram(String s, String t) {
    s = s.toLowerCase();
    t = t.toLowerCase();

    // We can ignore blanks
    char[] Word1 = s.replaceAll("\\s","").toCharArray();
    char[] Word2 = t.replaceAll("\\s","").toCharArray();

    // Anagrams length should be the same
    if (Word1.length != Word2.length) {
        return false;
    }

    // Sorting arrays is pretty fast, it can be O(logn) 
    Arrays.sort(Word1);
    Arrays.sort(Word2);

    if (Arrays.equals(Word1, Word2)) {
        return true;
    }

    return false;
}
1
pgardunoc

assurez-vous simplement que vous essayez de vérifier si s1 est un anagramme de s2 correct? Cela voudrait aussi dire que s2 est un anagramme de s1. Donc, je voudrais juste trier s1 et s2 et vérifier pour voir si elles sont égales. 

String string1 = "fdafdas";
String string2 = "fdwqkjl";
char[] chars = string1.toCharArray();
char[] chars2 = string2.toCharArray();
Arrays.sort(chars);
Arrays.sort(chars2);
string1 = new String(chars);
string2 = new String(chars2);
if (string1.equals(string2)) {
    //They are an anagram
}
0
user1103205
import Java.util.Scanner;

public class JavaProgram
{
    public static void main(String[] input)
    {
        String str1, str2;
        int len, len1, len2, i, j, found=0, not_found=0;
        Scanner scan = new Scanner(System.in);

        System.out.print("Enter First String : ");
        str1 = scan.nextLine();
        System.out.print("Enter Second String : ");
        str2 = scan.nextLine();

        len1 = str1.length();
        len2 = str2.length();

        if(len1 == len2)
        {
            len = len1;
            for(i=0; i<len; i++)
            {
                found = 0;
                for(j=0; j<len; j++)
                {
                    if(str1.charAt(i) == str2.charAt(j))
                    {
                        found = 1;
                        break;
                    }
                }
                if(found == 0)
                {
                    not_found = 1;
                    break;
                }
            }
            if(not_found == 1)
            {
                System.out.print("Strings are not Anagram to Each Other..!!");
            }
            else
            {
                System.out.print("Strings are Anagram");
            }
        }
        else
        {
            System.out.print("Both Strings Must have the same number of Character to be an Anagram");
        }
    }
}
0
Shiva Sagan

Je pense que cela fonctionne avec la complexité O (2n)

public static boolean isAnagram(String str1, String str2){
    if(str1.length() != str2.length()){ return false;}
    int[] buffer = new int[256];
    for(char ch : str1.toCharArray()){
        buffer[ch]++;
    }
    for(char ch : str2.toCharArray()){
        if(buffer[ch]==0) return false;
        buffer[ch] = (buffer[ch] > 0)?(buffer[ch] - 1 ): -1 ;   
    }
    return true;
}
0
Vishnu Vasudevan

J'ai pris le temps de bien comprendre la logique et d'écrire un code pour vérifier si deux chaînes de caractères sont des anagrammes ou non. Bien sûr, à l'aide des réponses ci-dessus! XD

public static void main(String[] args) {

    Map<Character, Integer> char_map = new HashMap<Character, Integer>();
    Map<Character, Integer> empty_map = new HashMap<Character, Integer>();
    String a = "HelloP";
    String b = "HePlol";

    if (a.length() != b.length()) {
        System.out.println("false");
        System.exit(0);
    }

    for (char c : a.toLowerCase().toCharArray()) {
        empty_map.put(c, 0);
        if (char_map.containsKey(c))
            char_map.put(c, 1 + char_map.get(c));
        else
            char_map.put(c, 1);
    }

    for (char c : b.toLowerCase().toCharArray())
        if (char_map.containsKey(c))
            char_map.put(c, char_map.get(c) - 1);

    System.out.println(char_map.equals(empty_map));
}
0
indieNik

Voici une approche plus simple reposant principalement sur Java . Le code complet est disponible ici https://github.com/rdsr/algorithms/blob/master/src/jvm/misc/AnagramsList.Java (Remarque: ceci résout un problème lié par un problème différent)

class Anagram {
    Map<Character, Integer> anagram;

    Anagram(String s) {
        anagram = new HashMap<Character, Integer>();

        for (final Character c : s.toCharArray()) {
            if (anagram.containsKey(c)) {
                anagram.put(c, 1 + anagram.get(c));
            } else {
                anagram.put(c, 1);
            }
        }
    }

    @Override
    public int hashCode() {
        //.. elided
    }

    @Override
    public boolean equals(Object obj) {
        //.. elided
    }
}


    public class Anagrams {
            public static void main(String[] args) {
                System.out.println(new Anagram("abc").equals(new Anagram("bac")));
            }
        }
0
RD.

une fois que j'ai fait un test pour un entretien d'embauche à propos d'anagrammes. Je l'ai téléchargé sur mon compte github. Le code Java vérifie si un fichier texte (avec plusieurs lignes) est un poème d'anagramme ou un texte d'anagramme.

https://github.com/javocsoft/anagram_poem_checker

J'espère que ça aide!

Au revoir.

0
JavocSoft

La raison en est simple, car la fonction replace crée un nouvel objet String. Cela ne fait rien à la chaîne réelle (dans votre cas, s2), car en Java, les chaînes sont finales par nature. Ainsi, comme l'a souligné cmonkey, vous supprimez toujours un caractère de votre chaîne s2, mais en réalité, un nouvel objet String est créé avec 1 caractère en moins, s2 reste tel quel. 

Pour ce faire, le moyen le plus simple consiste à créer un nouvel objet chaîne et à l’affecter à vous-même.

{
    s2=s2.replace(delString,"");
    ....
    if(s2.empty()) return true;
    return false;
}
0
Bhavya

Je suppose que la solution suivante a la complexité O(n), faites-moi savoir si quelqu'un diffère. 

import Java.util.HashMap;
import Java.util.Scanner;


public class Anagrams {

    static boolean isAnagram(String Word1, String Word2)
    {
        if(Word1.length() != Word2.length()) {
            return false;
        }
        int flag=0;
        HashMap<Character,Integer> table = new HashMap<Character,Integer>();
        for(int i=0; i< Word1.length();i++) {
            table.put(Word1.charAt(i),1);
        }

        for(int i=0; i< Word2.length();i++) {
            if(table.containsKey(Word2.charAt(i))) {
                continue;
            } else {
                flag=1;
                break;
            }   
        }
        return flag == 0;
    }

    public static void main(String[] args) {
        System.out.println("Enter your string");
        Scanner sc= new Scanner(System.in);
        String Word1= sc.nextLine();
        String Word2=sc.nextLine();

         boolean result = isAnagram(Word1,Word2);
         if(result) {
             System.out.println("The words are Anagrams");
         } else{
             System.out.println("The words are not Anagrams");   
         }
    }
}
0
Sahil Madan

Voici une autre suggestion sans initialiser un int [256] mais un int [26] pour l'alphabet anglais.

public static void main(String[] args) {
    System.out.println(isAnagram("restful", "fluster"));
}

static boolean isAnagram(String s1, String s2) {
    if (s1.length() != s2.length()) {
        return false;
    }
    int[] countArray = new int[26];
    for (int i = 0; i < s1.length(); i++) {
        countArray[getIndex(i, s1)]++;
        countArray[getIndex(i, s2)]--;
    }
    for (int i = 0; i < countArray.length; i++) {
        if (countArray[i] != 0) {
            return false;
        }
    }
    return true;
}

public static int getIndex(int position, String value) {
    return value.charAt(position) - 'a';
}

Meilleur George Tsopouridis

0
public static boolean isAnagram(String s1, String s2) {
    boolean aux = true;
    if (s1.length() != s2.length()){
        aux = false;
    }else{ 
        for (int i = 0; i < s1.length(); i++)
            if(s2.toUpperCase().indexOf(s1.toUpperCase().substring(i, i+1)) == -1) aux = false;
    }
    return aux;
}
0
serge

Voici ma solution de votre aspect

private static boolean isAnagram(String s1, String s2){
    int count = 0;
    boolean flag = false;

    if(s1.length() != s2.length()){
        return false;
    }
    //checks whether both Word's letters are the same
    for (int i = 0; i < s1.length(); i++){
        for (int j = 0; j < s2.length(); j++){
            if(s1.charAt(i) == s2.charAt(j)){
                count++;
                break;
            }
        }
    }
    //if count equals to one of the Strings length then it is an anagram
    if(count == s2.length() ){
        flag = true;
    }
    return flag;
}
0
Sabuhi Yusif
public class SampleAnagram {

    public static void main(String ...s) {


        String s1 = "work";
        String s2="kwro";

        System.out.println(s1.charAt(0));

        int s1L = s1.length();
        int s2L = s2.length();

        int totalCount = 0;

            for(int i=0;i<s1L;i++) {

                for(int j=0;j<s2L;j++) {

                if(s1.charAt(i)==(s2.charAt(j))) {

                    totalCount = totalCount+1;
                }
            }
        }

            System.out.println(totalCount);


            if(s1.length()==totalCount && s2.length()==totalCount) {

                System.out.println("given the input as anagram");
            }

    }



}
0
Dharma

Version plus rapide utilisant l'approche de vecteur de bits pour les sous-chaînes anagrammes

public boolean isAnagram(String _source1, String _source2)
{

    int flag = 0, char_index = 0, counter = 0;
    if(_source2.length() < _source1.length()){
        return false;
    }
    char[] _stringchar = _source1.toCharArray();
    char[] _tocheck = _source2.toCharArray();
    for(char character : _stringchar)
    {
        char_index = character - 'a';
        if((flag & (1 << char_index)) == 0)
            flag |= (1 << char_index);
    }

    for(char toCheckcChar : _tocheck)
    {
        char_index = toCheckcChar - 'a';

        if((flag & (1 << char_index)) > 0)
            counter++;
        else
            counter = 0;

        if(counter == _source1.length())
            return true;

    }

    return false;
}
0
Roshan
public boolean isAnagram(String a, String b) {

  boolean result = false;

  final String one = a.replaceAll("[\\s+\\W+]", "").toLowerCase();

  final String two = b.replaceAll("[\\s+\\W+]", "").toLowerCase();

  if (one.length() == two.length()) {

      final char[] oneArray =  one.toCharArray();

      final char[] twoArray =  two.toCharArray(); 

      Arrays.sort(oneArray);

      Arrays.sort(twoArray);

      result = Arrays.equals(oneArray, twoArray);

    }

  return result; 

}
0
user2018144
    String str1="Mother In Law";
    String str2="Hitler Woman";
    char[] anag1=str1.replaceAll("\\s", "").toLowerCase().toCharArray();
    char[] anag2=str2.replaceAll("\\s", "").toLowerCase().toCharArray();
    Arrays.sort(anag1);
    Arrays.sort(anag2);
    System.out.println(Arrays.equals(anag1, anag2)? "words are anagrams":"words are not anagrams");
0
rathangangaa
public class Anagram {
    public boolean isAnagram(
            String left, 
            String right) {
        if (left.length() == right.length()) {
            Map<Character, Integer> map = new HashMap<>();
            char[] a = left.toCharArray(), b = right.toCharArray();
            for (int i = 0; i < a.length; i++) {
                accumulate(map, a[i]);
                accumulate(map, b[i]);
            }
            for (char c : map.keySet()) {
                if (map.get(c) > 0) {
                    return false;
                }
            }
            return true;
        } else {
            return false;
        }
    }

    private void accumulate(
            Map<Character, Integer> map, 
            char key) {
        if (map.containsKey(key)) {
            map.put(key, Math.abs(map.get(key) - 1));
        } else {
            map.put(key, 1);
        }
    }
}
0
Henry Orunkoya

Il existe différentes solutions pour déterminer si une chaîne est Anagram ou non. 1. en utilisant la méthode prédéfinie Array.sort()

String string1 = "abc";
String string2 = "bca";
char[] chars = string1.toCharArray();
char[] chars2 = string2.toCharArray();
Arrays.sort(chars);
Arrays.sort(chars2);
string1 = new String(chars);
string2 = new String(chars2);
if (string1.equalsIgnoreCase(string2)) {
  System.out.println("Anagram");
} else {
  System.out.println("Not Anagram");
}

Complexité temporelle: Ω(n log n) 2. Par méthode itérative

char [] charArray = str.toCharArray();
if(str.length() == str1.length()){
    for(char ch : charArray){
        if(str1.indexOf(ch) == -1){
            System.out.println("Not Anagram");
        } 
    }    
    System.out.println("Anagram");
} else {
    System.out.println("Not Anagram");
}

Complexité temporelle: Ω(n)

Bien que le premier algorithme soit plus lisible, le deuxième algorithme s'exécute vraiment plus rapidement.

0
Ram Bhandari

Ceci est une implémentation Java que j'ai écrite pour utiliser un tableau au lieu d'un HashMap. Cela économise de l'espace, et les tableaux sont très rapides.

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

        int[] arr = new int[123];
        for (char c : s.toCharArray())
            arr[c]++;
        for (char c : t.toCharArray())
            arr[c]--;
        for (int i : arr)
            if (i != 0)
                return false;
        return true;
    }
0
Jing Ma

O (n) solution sans aucun type de tri et en utilisant une seule carte. Également ajouté le contrôle nul approprié manquant dans d'autres solutions. 

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:

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

il suffit de voir à la ligne newStr = s2.replace (delStr, "");

ce que vous faites ici remplacer un caractère dans s2 et réattribuer à newStr signifie que vous ne changez rien dans s2. Il suffit de remplacer ce code par le code ci-dessous qui fonctionnera correctement

newStr = newStr.replace (delStr, "");

0
RKP