java-string-Permutationen und Kombinationen-lookup
Schreibe ich eine Android word-app. Mein code enthält eine Methode finden würde, dass alle Kombinationen von Strings und Teilstrings eines 7 Buchstaben-Zeichenfolge mit mindestens der Länge 3. Dann vergleichen Sie alle verfügbaren Kombination zu jedem Wort im Wörterbuch zu finden, der alle gültigen Wörter. Ich bin mit einer rekursiven Methode. Hier ist der code.
//Gets all the permutations of a string.
void permuteString(String beginningString, String endingString) {
if (endingString.length() <= 1){
if((Arrays.binarySearch(mDictionary, beginningString.toLowerCase() + endingString.toLowerCase())) >= 0){
mWordSet.add(beginningString + endingString);
}
}
else
for (int i = 0; i < endingString.length(); i++) {
String newString = endingString.substring(0, i) + endingString.substring(i + 1);
permuteString(beginningString + endingString.charAt(i), newString);
}
}
//Get the combinations of the sub-strings. Minimum 3 letter combinations
void subStrings(String s){
String newString = "";
if(s.length() > 3){
for(int x = 0; x < s.length(); x++){
newString = removeCharAt(x, s);
permuteString("", newString);
subStrings(newString);
}
}
}
Der obige code läuft wunderbar, aber wenn ich es installiert auf meinem Nexus s habe ich realisiert, dass es läuft ein bisschen zu langsam. Es dauert ein paar Sekunden. Etwa 3 oder 4 Sekunden, das ist inakzeptabel.
Jetzt habe ich gespielt einige Wort-Spiele auf meinem Handy und Sie berechnen alle Kombinationen einer string sofort das lässt mich glauben, dass mein Algorithmus ist nicht sehr effizient, und es kann verbessert werden. Kann mir jemand helfen?
public class TrieNode {
TrieNode 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;
TrieNode[] children = {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};
private ArrayList<String> words = new ArrayList<String>();
public void addWord(String word){
words.add(word);
}
public ArrayList<String> getWords(){
return words;
}
}
public class Trie {
static String myWord;
static String myLetters = "afinnrty";
static char[] myChars;
static Sort sort;
static TrieNode myNode = new TrieNode();
static TrieNode currentNode;
static int y = 0;
static ArrayList<String> availableWords = new ArrayList<String>();
public static void main(String[] args) {
readWords();
getPermutations();
}
public static void getPermutations(){
currentNode = myNode;
for(int x = 0; x < myLetters.length(); x++){
if(currentNode.children[myLetters.charAt(x) - 'a'] != null){
//availableWords.addAll(currentNode.getWords());
currentNode = currentNode.children[myLetters.charAt(x) - 'a'];
System.out.println(currentNode.getWords() + "" + myLetters.charAt(x));
}
}
//System.out.println(availableWords);
}
public static void readWords(){
try {
BufferedReader in = new BufferedReader(new FileReader("c://scrabbledictionary.txt"));
String str;
while ((str = in.readLine()) != null) {
myWord = str;
myChars = str.toCharArray();
sort = new Sort(myChars);
insert(myNode, myChars, 0);
}
in.close();
} catch (IOException e) {
}
}
public static void insert(TrieNode node, char[] myChars, int x){
if(x >= myChars.length){
node.addWord(myWord);
//System.out.println(node.getWords()+""+y);
y++;
return;
}
if(node.children[myChars[x]-'a'] == null){
insert(node.children[myChars[x]-'a'] = new TrieNode(), myChars, x=x+1);
}else{
insert(node.children[myChars[x]-'a'], myChars, x=x+1);
}
}
}
Du musst angemeldet sein, um einen Kommentar abzugeben.
In Ihrem aktuellen Ansatz, Sie suchen jede permutation der einzelnen Teilstrings. Also für
"abc"
Sie nachschlagen müssen"abc"
,"acb"
,"bac"
,"bca"
,"cab"
und"cba"
. Wenn Sie wollte, zu finden, die alle Permutationen von "Permutationen", die Anzahl der Suchvorgänge ist fast 500,000,000, und das ist, bevor Sie haben sogar sah in seine Teilstrings. Aber wir können reduzieren diese zu eine lookup, unabhängig von der Länge, von der Vorverarbeitung Wörterbuch.Die Idee ist, jedes Wort in das Wörterbuch, in einigen Daten-Struktur, wobei jedes element enthält eine Reihe von Zeichen, und eine Liste aller Wörter, die (nur) diejenigen Zeichen. So zum Beispiel, könnten Sie bauen einen binären Baum, der hätte einen Knoten mit den (sortiert) Zeichensatz
"abd"
und das Wort Liste["bad", "dab"]
. Nun, wenn wir wollen finden, dass alle Permutationen von"dba"
, Sortieren wir es zu geben"abd"
und schauen Sie sich in den Baum, um die Liste abzurufen.Als Baumann wies darauf hin, versucht sind gut geeignet zur Speicherung dieser Art von Daten. Die Schönheit des trie ist, dass die lookup-Zeit hängt nur von der Länge des Suchstrings - es ist unabhängig von der Größe Ihres Wörterbuchs. Da werden Sie speichern sehr viel von Worten, und die meisten von Ihre Suchbegriffe werden klein sein (die Mehrheit der 3-Zeichen-Teilfolgen von der niedrigsten Ebene Ihres Rekursion), diese Struktur ist ideal.
In diesem Fall die Pfade auf Ihre versuche wider, die Zeichen setzt eher als die Worte selbst. Also, wenn Sie Ihre gesamte Wörterbuch war
["bad", "dab", "cab", "cable"]
, lookup-Struktur würde am Ende Aussehen wie diese:Es ist ein bisschen von einer Raum/Zeit-trade-off in der Weise, die Sie dies umsetzen. Im einfachsten (und schnellsten) Ansatz, jede
Node
enthält nur die Liste von Wörtern, und eine ReiheNode[26]
von Kindern. Dies ermöglicht Ihnen, suchen Sie das Kind, bist du nach in konstanter Zeit, einfach durch einen Blick aufchildren[s.charAt(i)-'a']
(wos
ist dein Suchbegriff undi
ist Ihre aktuelle Tiefe in der Marina).Der Nachteil ist, dass die meisten Ihrer
children
arrays sind meistens leer. Wenn der Raum ist ein Thema, können Sie eine kompaktere Darstellung, wie eine verknüpfte Liste, dynamischen Arrays, hash-Tabelle, etc. Jedoch, diese auf Kosten der potentiell erfordert mehrere Speicherzugriffe und Vergleiche bei jedem Knoten, statt der einfachen array-Zugriff von oben. Aber ich wäre überrascht, wenn der Platz verschwendet wurde mehr als ein paar Megabyte über das gesamte Wörterbuch, so dass die array-basierte Ansatz ist wahrscheinlich Ihre beste Wette.Mit der Marina im Ort, Ihre ganze permutation-Funktion ersetzt wird, die mit einer lookup, womit sich die Komplexität nach unten aus O(N! log D) (wo D ist die Größe des Wörterbuchs, N die Größe des Strings) zu O(N log N) (da müssen Sie zum Sortieren der Zeichen; die Suche selbst ist O(N)).
EDIT: ich geworfen haben zusammen eine (ungetestete) Implementierung dieser Struktur: http://pastebin.com/Qfu93E80
permuteString()
Funktion, aber IhresubStrings()
Funktion sonst bleiben die gleichen. Sie könnte build all diese Informationen in Ihre versuche (addiert man alle Worte für alle Teilfolgen in jedem Knoten), aber es wird viel länger dauern, um zu bauen, viel mehr Speicher, und wird sich nicht verbessern Ihre worst-case-performance. Aber wenn die version, die ich gegeben habe ist immer noch zu langsam, lassen Sie mich wissen und ich werde versuchen, näher auf diese alternative.TrieNode[26]
mit einemArrayList<TrieNode>
. Tun Sie es direkt von der SD-Karte würde schwierig sein, und viel langsamer. By the way, wie viel Speicher verfügbar ist, um Ihre Anwendung? Wie groß ist dein Wörterbuch? Wie viele TrieNodes erstellt werden? Läuft es out of memory beim Bau der Marina, oder woanders?Siehe hier: So finden Sie Liste der möglichen Wörter aus einer Buchstaben-matrix [Boggle Solver]
Die Idee, die hinter dem code in die Antworten ist wie folgt:
Ich glaube nicht, dass das hinzufügen alle Permutationen notwendig ist. Sie können einfach Kapseln die Zeichenfolge in eine
PermutationString
:Einen
PermutationString
ist ein string, aber wo zweiPermutationString
s sind gleich, wenn Sie die gleiche Frequenz von Zeichen. Sonew PermutationString("bad").equals(new PermutationString("dab"))
. Dies gilt auch für die.hashCode()
: wenn die Streicher sind Permutationen voneinander, Sie erzeugen die gleiche.hashCode()
.Nun können Sie einfach eine
HashMap<PermutationString,ArrayList<String>>
wie folgt:So, nun sind wir iterieren über alle möglichen Wörter im Wörterbuch, konstruieren Sie eine
PermutationString
als Schlüssel, und wenn die Schlüssel bereits vorhanden ist (das heißt, es gibt bereits ein Wort mit dem gleichen Zeichen Frequenzen), fügen wir einfach unser eigenes Wort zu ihm. Andernfalls fügen wir eine neueArrayList<String>
mit dem einzigen Wort.Nun haben wir gefüllt, bis das
hm
mit allen Permutationen (aber nicht so viel Schlüssel) können Sie Abfragen:Diese zurück
ArrayList<String>
mit"foo"
und"oof"
.Testcase:
Verwenden Trie
Anstelle der Prüfung alle N! Möglichkeiten, Sie Folgen nur Präfix-Bäumen, führen zu einem Ergebnis. Dies wird significanlty reduzieren die Menge der Zeichenfolgen, die Sie überprüft haben, gegen.
Gut, Sie erweitern Ihr Wörterbuch Entitäten mit array
letters[]
woletters[i]
bleibt für Zeiten, die i-te Buchstabe des Alphabets in diesem Wort. Es werden einige zusätzliche Speicher nicht weit, als es die jetzt verwendet wird.Dann für jedes Wort die Permutationen, die Sie überprüfen möchten, werden Sie brauchen, um count-Anzahl der unterschiedlichen Buchstaben zu und dann traverse durch dictiory mit einfachem Vergleich Verfahren. Wenn für alle Buchstaben für Wort aus dem Wörterbuch Zahl der Ereignisse weniger oder gleich für Wort prüfen wir - ja, dieses Wort dargestellt werden kann als permutation von substring, sonst - Nein.
Komplexität: es nahm O(D * maxLen) für die Vorberechnung und O(max(N, D)) für jede Abfrage.
letters[]
array; wenn Sie Art Ihrem Wörterbuch nach, um diese arrays können Sie finden, die Sie suchen, in O(logD). Das ist ziemlich viel, was meine Lösung oben tut.