Deux mots sont des anagrammes si l’un d’eux a exactement les mêmes caractères que celui d’un autre mot.
Exemple: Anagram
& Nagaram
sont des anagrammes (insensible à la casse).
Maintenant, il y a beaucoup de questions similaires à celle-ci. Quelques approches pour déterminer si deux chaînes sont des anagrammes sont les suivantes:
1) Sort
les chaînes et les comparer.
2) Créez un frequency map
pour ces chaînes et vérifiez si elles sont identiques ou non.
Mais dans ce cas, on nous donne un mot (par souci de simplicité, supposons un mot unique et il n’aura que des anagrammes de mots uniques) et nous devons trouver des anagrammes pour cela.
La solution à laquelle je pense est que nous pouvons générer toutes les permutations pour le mot et vérifier lequel de ces mots existe dans le dictionnaire . Mais clairement, c'est très inefficace. Oui, le dictionnaire est également disponible.
Alors, quelles alternatives avons-nous ici?
J'ai également lu dans un fil de discussion similaire que quelque chose peut être fait en utilisant Tries
mais la personne n'a pas expliqué en quoi consistait l'algorithme et pourquoi avons-nous utilisé un Trie en premier lieu; Donc, ce n'était pas vraiment utile, c'est pourquoi j'ai créé ce nouveau fil. Si quelqu'un veut partager son implémentation (autre que C, C++ ou Java), merci de l'expliquer.
Exemple d'algorithme:
Open dictionary
Create empty hashmap H
For each Word in dictionary:
Create a key that is the Word's letters sorted alphabetically (and forced to one case)
Add the Word to the list of words accessed by the hash key in H
Pour vérifier toutes les anagrammes d'un mot donné:
Create a key that is the letters of the Word, sorted (and forced to one case)
Look up that key in H
You now have a list of all anagrams
Relativement rapide à construire, extrêmement rapide en recherche.
J'ai imaginé une nouvelle solution. Il utilise le théorème fondamental de l'arithmétique. L'idée est donc d'utiliser un tableau des 26 premiers nombres premiers. Ensuite, pour chaque lettre du mot d'entrée, nous obtenons le nombre premier correspondant A = 2, B = 3, C = 5, D = 7… et nous calculons ensuite le produit de notre mot d'entrée. Ensuite, nous le faisons pour chaque mot du dictionnaire et si un mot correspond à notre mot d’entrée, nous l’ajoutons à la liste résultante. Tous les anagrammes auront la même signature car
Tout nombre entier supérieur à 1 est un nombre premier ou peut être écrit comme un produit unique de nombres premiers (en ignorant la commande).
Voici le code. Je convertis le mot en MAJUSCULES et 65 correspond à la position de A qui correspond à mon premier nombre premier:
private int[] PRIMES = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31,
37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103,
107, 109, 113 };
C'est la méthode:
private long calculateProduct(char[] letters) {
long result = 1L;
for (char c : letters) {
if (c < 65) {
return -1;
}
int pos = c - 65;
result *= PRIMES[pos];
}
return result;
}
Nous savons que si deux mots n'ont pas la même longueur, ils ne sont pas des anagrammes. Vous pouvez donc partitionner votre dictionnaire en groupes de mots de même longueur.
Nous nous concentrons maintenant sur un seul de ces groupes et, fondamentalement, tous les mots ont exactement la même longueur dans cet univers plus petit.
Si chaque position de la lettre est une dimension et que la valeur de cette dimension est basée sur la lettre (dites le code ASCII). Ensuite, vous pouvez calculer la longueur du vecteur Word.
Par exemple, dites 'A' = 65, 'B' = 66, puis length("AB") = sqrt(65*65 + 66*66)
. De toute évidence, length("AB") = length("BA")
.
Clairement, si deux mots sont des anagrammes, leurs vecteurs ont la même longueur. La question suivante est la suivante: si deux vecteurs Word (de même nombre de lettres) ont la même longueur, sont-ils des anagrammes? Intuitivement, je dirais non, car tous les vecteurs de cette longueur forment une sphère, ils sont nombreux. Pas sûr, puisque nous sommes dans l'espace entier dans ce cas, combien y en a-t-il réellement.
Mais à tout le moins, cela vous permet de partitionner votre dictionnaire encore plus loin. Pour chaque mot de votre dictionnaire, calcule la distance du vecteur: for(each letter c) { distance += c*c }; distance = sqrt(distance);
Créez ensuite une carte pour tous les mots de longueur n
et saisissez-la avec la distance. La valeur est une liste de mots de longueur n
qui donnent cette distance particulière.
Vous allez créer une carte pour chaque distance.
Ensuite, votre recherche devient l'algorithme suivant:
static void Main(string[] args)
{
string str1 = "Tom Marvolo Riddle";
string str2 = "I am Lord Voldemort";
str2= str2.Replace(" ", string.Empty);
str1 = str1.Replace(" ", string.Empty);
if (str1.Length != str2.Length)
Console.WriteLine("Strings are not anagram");
else
{
str1 = str1.ToUpper();
str2 = str2.ToUpper();
int countStr1 = 0;
int countStr2 = 0;
for (int i = 0; i < str1.Length; i++)
{
countStr1 += str1[i];
countStr2 += str2[i];
}
if(countStr2!=countStr1)
Console.WriteLine("Strings are not anagram");
else Console.WriteLine("Strings are anagram");
}
Console.Read();
}
clojure.string/lower-case
).group-by
) par lettre-fréquence (frequencies
).(These
) sont les fonctions correspondantes dans le dialecte LISP Clojure.
La fonction entière peut être exprimée ainsi:
(defn anagrams [dict]
(->> dict
(map clojure.string/lower-case)
(group-by frequencies)
vals))
Par exemple,
(anagrams ["Salt" "last" "one" "eon" "plod"])
;(["salt" "last"] ["one" "eon"] ["plod"])
Une fonction d’indexation qui associe chaque élément à sa collection est
(defn index [xss]
(into {} (for [xs xss, x xs] [x xs])))
Pour que, par exemple,
((comp index anagrams) ["Salt" "last" "one" "eon" "plod"])
;{"salt" ["salt" "last"], "last" ["salt" "last"], "one" ["one" "eon"], "eon" ["one" "eon"], "plod" ["plod"]}
... où comp
est l'opérateur de composition fonctionnelle.
Tentez bien, il serait plus facile de vérifier si le mot existe ..__ Donc, si vous mettez tout le dictionnaire dans un trie:
http://en.wikipedia.org/wiki/Trie
vous pouvez ensuite prendre votre mot et faire un retour en arrière simple en prenant un caractère et en vérifiant récursivement si nous pouvons "marcher" sur le Trie avec une combinaison du reste des caractères (en ajoutant un caractère à la fois). Lorsque tous les caractères sont utilisés dans une branche de récursivité et qu'il existe un chemin valide dans le Trie, le mot existe.
Le Trie aide car c'est une condition d'arrêt de Nice: Nous pouvons vérifier si la partie d'une chaîne, par exemple "Anag" est un chemin valide dans le fichier, sinon nous pouvons casser cette branche récursive. Cela signifie que nous n'avons pas à vérifier chaque permutation des personnages.
En pseudo-code
checkAllChars(currentPositionInTrie, currentlyUsedChars, restOfWord)
if (restOfWord == 0)
{
AddWord(currentlyUsedChar)
}
else
{
foreach (char in restOfWord)
{
nextPositionInTrie = Trie.Walk(currentPositionInTrie, char)
if (nextPositionInTrie != Positions.NOT_POSSIBLE)
{
checkAllChars(nextPositionInTrie, currentlyUsedChars.With(char), restOfWord.Without(char))
}
}
}
Évidemment, vous avez besoin d'une structure de données Nice Trie qui vous permet de "parcourir" progressivement l'arbre et de vérifier à chaque nœud s'il existe un chemin d'accès avec le caractère donné vers un nœud suivant ...
essayé de mettre en œuvre la solution hashmap
public class Dictionary {
public static void main(String[] args){
String[] Dictionary=new String[]{"dog","god","tool","loot","rose","sore"};
HashMap<String,String> h=new HashMap<String, String>();
QuickSort q=new QuickSort();
for(int i=0;i<Dictionary.length;i++){
String temp =new String();
temp= q.quickSort(Dictionary[i]);//sorted Word e.g dgo for dog
if(!h.containsKey(temp)){
h.put(temp,Dictionary[i]);
}
else
{
String s=h.get(temp);
h.put(temp,s + " , "+ Dictionary[i]);
}
}
String Word=new String(){"tolo"};
String sortedword = q.quickSort(Word);
if(h.containsKey(sortedword.toLowerCase())){ //used lowercase to make the words case sensitive
System.out.println("anagrams from Dictionary : " + h.get(sortedword.toLowerCase()));
}
}
Cela dépend de la façon dont vous stockez votre dictionnaire. S'il s'agit d'un simple tableau de mots, aucun algorithme ne sera plus rapide que linéaire.
Si le problème est résolu, voici une approche qui peut fonctionner. Je l'ai inventé tout à l'heure, mais je suppose que c'est plus rapide que l'approche linéaire.
Voici comment je le ferais, de toute façon. Il devrait y avoir une approche plus conventionnelle, mais elle est plus rapide que linéaire.
projetez le vecteur de comptage de chaque mot du dictionnaire dans cette direction aléatoire et stockez la valeur (insérez-la de sorte que le tableau de valeurs soit trié).
Avec un nouveau mot de test, projetez-le dans la même direction aléatoire que celle utilisée pour les mots du dictionnaire.
PS: La procédure ci-dessus est une généralisation de la procédure des nombres premiers pouvant potentiellement conduire à des nombres élevés (et donc à des problèmes de précision de calcul).
Générer toutes les permutations est facile, je suppose que vous craignez que vérifier leur existence dans le dictionnaire soit la partie "très inefficace". Mais cela dépend en fait de la structure de données que vous utilisez pour le dictionnaire: bien entendu, une liste de mots serait inefficace pour votre cas d'utilisation. En parlant de Essais , ils seraient probablement une représentation idéale et très efficace également.
Une autre possibilité consisterait à effectuer un pré-traitement sur votre dictionnaire, par exemple. construisez une table de hachage où les clés sont les lettres du mot triées et les valeurs sont des listes de mots. Vous pouvez même sérialiser cette hashtable pour pouvoir l'écrire dans un fichier et recharger rapidement plus tard. Ensuite, pour rechercher des anagrammes, il vous suffit de trier votre mot et de rechercher l'entrée correspondante dans la table de hachage.