it-swarm.com.de

Anagramme für ein bestimmtes Wort finden

Zwei Wörter sind Anagramme, wenn eines von ihnen genau dieselben Zeichen wie das andere Wort hat.

Beispiel: Anagram & Nagaram sind Anagramme (Groß- und Kleinschreibung wird nicht berücksichtigt).

Nun gibt es viele ähnliche Fragen. Es gibt mehrere Ansätze, um herauszufinden, ob zwei Zeichenfolgen Anagramme sind:

1)Sort die Zeichenketten und vergleichen Sie sie.

2) Erstellen Sie einen frequency map für diese Zeichenfolgen und prüfen Sie, ob sie gleich sind oder nicht.

In diesem Fall erhalten wir jedoch ein Wort (zur Vereinfachung nehmen wir nur ein einziges Wort an, und es wird nur einzelne Wortanagramme enthalten), und wir müssen dazu Anagramme finden. 

Eine Lösung, die ich mir vorstelle, ist, dass wir alle Permutationen generieren für das Wort erstellen und prüfen können, welches dieser Wörter im Wörterbuch vorhanden ist. Dies ist jedoch eindeutig ineffizient. Ja, das Wörterbuch ist auch verfügbar.

Welche Alternativen haben wir hier?

Ich habe auch in einem ähnlichen Thread gelesen, dass mit Tries etwas getan werden kann, aber die Person hat nicht erklärt, was der Algorithmus war und warum wir an erster Stelle einen Trie verwendet haben. Das war also nicht wirklich hilfreich, weshalb ich diesen neuen Thread erstellt habe. Wenn jemand seine Implementierung (außer C, C++ oder Java) teilen möchte, erklären Sie dies bitte auch.

36
h4ck3d

Beispielalgorithmus:

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

So überprüfen Sie alle Anagramme eines bestimmten Wortes:

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

Relativ schnell zu bauen, blitzschnell beim Nachschlagen.

70
Vatine

Ich habe eine neue Lösung gefunden, denke ich. Es verwendet den Fundamentalsatz der Arithmetik. Die Idee ist also, ein Array der ersten 26 Primzahlen zu verwenden. Dann erhalten wir für jeden Buchstaben im Eingabewort die entsprechende Primzahl A = 2, B = 3, C = 5, D = 7… und berechnen dann das Produkt unseres Eingabeworts. Als Nächstes tun wir dies für jedes Wort im Wörterbuch. Wenn ein Wort mit unserem Eingabewort übereinstimmt, fügen wir es der Ergebnisliste hinzu. Alle Anagramme haben da die gleiche Signatur 

Jede ganze Zahl größer als 1 ist entweder eine Primzahl oder kann geschrieben werden als einzigartiges Produkt von Primzahlen (Ignorieren der Reihenfolge).

Hier ist der Code. Ich konvertiere das Wort in GROSSBUCHSTABEN und 65 ist die Position von A, die meiner ersten Primzahl entspricht:

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

Dies ist die Methode:

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

Wir wissen, dass zwei Wörter keine Anagramme sind, wenn zwei Wörter nicht die gleiche Länge haben. So können Sie Ihr Wörterbuch in Wortgruppen gleicher Länge unterteilen.

Jetzt konzentrieren wir uns nur auf eine dieser Gruppen und im Grunde haben alle Wörter in diesem kleineren Universum genau die gleiche Länge.

Wenn jede Buchstabenposition eine Dimension ist und der Wert in dieser Dimension auf dem Buchstaben basiert (sagen Sie den Code ASCII). Dann können Sie die Länge des Word-Vektors berechnen.

Sagen Sie zum Beispiel 'A' = 65, 'B' = 66 und dann length("AB") = sqrt(65*65 + 66*66). Offensichtlich length("AB") = length("BA").

Wenn zwei Word Anagramme sind, haben ihre Vektoren die gleiche Länge. Die nächste Frage ist, wenn zwei Word-Vektoren (mit derselben Anzahl von Buchstaben) die gleiche Länge haben, sind es Anagramme? Intuitiv würde ich nein sagen, da alle Vektoren mit dieser Länge eine Kugel bilden, gibt es viele. Nicht sicher, da wir uns in diesem Fall im ganzzahligen Bereich befinden, wie viele tatsächlich vorhanden sind.

Aber zumindest können Sie Ihr Wörterbuch noch weiter partitionieren. Berechnen Sie für jedes Wort in Ihrem Wörterbuch die Entfernung des Vektors: for(each letter c) { distance += c*c }; distance = sqrt(distance);

Erstellen Sie dann eine Karte für alle Wörter der Länge n und geben Sie sie mit der Entfernung ein. Der Wert ist eine Liste von Wörtern der Länge n, die diese bestimmte Entfernung ergeben.

Sie erstellen für jede Entfernung eine Karte.

Dann wird Ihr Lookup zum folgenden Algorithmus:

  1. Verwenden Sie die richtige Wörterbuchzuordnung basierend auf der Länge des Wortes
  2. Berechnen Sie die Länge des Vektors Ihres Wortes
  3. Suchen Sie die Liste der Wörter, die dieser Länge entsprechen
  4. Gehen Sie die Liste durch und wählen Sie die Anagramme mit einem naiven Algorithmus aus. Jetzt ist die Liste der Kandidaten stark reduziert
2
mprivat
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();
}
1
KrtkNyk
  • Reduzieren Sie die Wörter auf - etwa - Kleinbuchstaben (clojure.string/lower-case).
  • Klassifizieren Sie sie (group-by) nach Buchstaben-Frequenzzuordnung (frequencies).
  • Lass die Frequenzkarten fallen, 
  • ... die Anagrammsammlung verlassen.

(These) sind die entsprechenden Funktionen im LISP-Dialekt Clojure. 

Die ganze Funktion kann so ausgedrückt werden: 

(defn anagrams [dict]
  (->> dict
       (map clojure.string/lower-case)
       (group-by frequencies)
       vals))

Zum Beispiel, 

(anagrams ["Salt" "last" "one" "eon" "plod"])
;(["salt" "last"] ["one" "eon"] ["plod"])

Eine Indizierungsfunktion, die jedes Objekt seiner Sammlung zuordnet, ist

(defn index [xss]
  (into {} (for [xs xss, x xs] [x xs])))

So dass zum Beispiel 

((comp index anagrams) ["Salt" "last" "one" "eon" "plod"])
;{"salt" ["salt" "last"], "last" ["salt" "last"], "one" ["one" "eon"], "eon" ["one" "eon"], "plod" ["plod"]}

... wobei comp der Operator für die funktionale Komposition ist. 

1
Thumbnail

Nun, Tries würde es einfacher machen, zu prüfen, ob das Wort existiert. Wenn Sie also das gesamte Wörterbuch in einen Trie schreiben:

http://en.wikipedia.org/wiki/Trie

danach können Sie Ihr Wort nehmen und ein einfaches Backtracking durchführen, indem Sie ein Zeichen nehmen und rekursiv prüfen, ob wir den Trie mit einer beliebigen Kombination der übrigen Zeichen "laufen" können (indem Sie jeweils ein Zeichen hinzufügen). Wenn alle Zeichen in einem Rekursionszweig verwendet werden und der Trie einen gültigen Pfad enthält, ist das Wort vorhanden.

Der Trie ist hilfreich, da dies eine Nice-Stoppbedingung ist: Wir können überprüfen, ob der Teil einer Zeichenfolge, z. B. "Anag", ein gültiger Pfad im Trie ist. Wenn nicht, können wir den entsprechenden rekursiven Zweig aufbrechen. Dies bedeutet, dass wir nicht jede einzelne Permutation der Zeichen prüfen müssen.

Im Pseudocode

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

Natürlich benötigen Sie eine Nice-Trie-Datenstruktur, die es Ihnen ermöglicht, den Baum schrittweise "durchzugehen" und an jedem Knoten zu prüfen, ob es einen Pfad mit dem angegebenen Zeichen zu einem nächsten Knoten gibt ... 

1
Daniel

versuchte, die Hashmap-Lösung zu implementieren

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

}
0
megha

Das hängt davon ab, wie Sie Ihr Wörterbuch speichern. Wenn es sich um ein einfaches Array von Wörtern handelt, ist kein Algorithmus schneller als linear.

Wenn es sortiert ist, ist hier ein Ansatz, der funktionieren kann. Ich habe es gerade erst erfunden, aber ich denke, es ist schneller als linear.

  1. Bezeichnen Sie Ihr Wörterbuch als D, das aktuelle Präfix als S. S = 0;
  2. Sie erstellen eine Frequenzkarte für Ihr Word. Lasst es mit F. bezeichnen.
  3. Verwenden der binären Suche Suchzeiger zum Anfang jedes Buchstabens im Wörterbuch. Dieses Feld von Zeigern wird mit P bezeichnet.
  4. Überspringen Sie für jedes Zeichen c von A bis Z, wenn F [c] == 0, sonst
    • S + = c;
    • F [c] -;
    • P <- für jedes Zeichen i P [i] = Zeiger auf das erste Wort, das mit S + i beginnt.
    • Rufen Sie Schritt 4 rekursiv auf, bis Sie eine Übereinstimmung für Ihr Wort finden oder bis Sie feststellen, dass keine solche Übereinstimmung existiert.

So würde ich es sowieso tun. Es sollte einen konventionelleren Ansatz geben, dieser ist jedoch schneller als linear.

0
Saage
  • Berechnen Sie den Frequenzzählungsvektor für jedes Wort im Wörterbuch, einen Längenvektor der Alphabetliste.
  • erzeugen Sie einen zufälligen Gaußschen Vektor der Länge der Alphabetliste
  • projizieren Sie den Zählvektor jedes Wörterbuchs in diese zufällige Richtung und speichern Sie den Wert (fügen Sie ihn so ein, dass das Array von Werten sortiert wird).

  • Projizieren Sie ein neues Testwort in dieselbe zufällige Richtung, die für die Wörterbuchwörter verwendet wird.

  • Führen Sie eine binäre Suche durch, um die Liste der Wörter zu finden, die demselben Wert zugeordnet sind.
  • Prüfen Sie, ob jedes Wort, das Sie wie oben erhalten haben, tatsächlich ein wahres Anagramm ist. Wenn nicht, entfernen Sie es aus der Liste.
  • Gibt die restlichen Elemente der Liste zurück.

PS: Das obige Verfahren ist eine Verallgemeinerung des Primzahlverfahrens, das möglicherweise zu einer großen Anzahl (und damit zu Problemen mit der Rechengenauigkeit) führen kann.

0
Vedarun

Das Generieren aller Permutationen ist einfach. Ich denke, Sie machen sich Sorgen, dass das Überprüfen ihrer Existenz im Wörterbuch der "höchst ineffiziente" Teil ist. Das hängt jedoch tatsächlich davon ab, welche Datenstruktur Sie für das Wörterbuch verwenden: Natürlich wäre eine Liste von Wörtern für Ihren Anwendungsfall ineffizient. Apropos Versuche , sie wären wahrscheinlich eine ideale Darstellung und auch ziemlich effizient.

Eine andere Möglichkeit wäre die Vorverarbeitung Ihres Wörterbuchs, z. Erstellen Sie eine Hashtabelle, in der die Schlüssel die sortierten Buchstaben des Wortes sind und die Werte Wortlisten sind. Sie können diese Hashtabelle sogar serialisieren, sodass Sie sie in eine Datei schreiben und später schnell erneut laden können. Um Anagramme nachzuschlagen, sortieren Sie einfach Ihr angegebenes Wort und suchen den entsprechenden Eintrag in der Hashtabelle.

0
Artyom