// 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); } } }
上面的代码运行正常,但是当我将其安装在我的Nexus上时,我意识到它运行有点太慢了.完成需要几秒钟.大概3或4秒是不能接受的.
现在我已经在手机上玩过一些文字游戏,他们会立即计算一个字符串的所有组合,这使我相信我的算法效率不高,可以改进.谁能帮忙?
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,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(),x=x+1); }else{ insert(node.children[myChars[x]-'a'],x=x+1); } } }
解决方法
这个想法是将字典中的每个单词都放入一些数据结构,其中每个元素包含一组字符,以及包含(仅)这些字符的所有单词的列表.因此,例如,您可以构建一个二叉树,它将包含一个包含(排序)字符集“abd”和单词列表[“bad”,“dab”]的节点.现在,如果我们要查找“dba”的所有排列,我们将其排序为“abd”,并将其查找到树中以检索列表.
正如鲍曼指出的那样,tries非常适合存储这种数据. trie的优点是查找时间仅取决于您的搜索字符串的长度 – 它与您的字典的大小无关.由于您将存储相当多的单词,并且大多数搜索字符串将很小(大部分将是您的递归最低级别的3个字符的子串),这种结构是理想的.
在这种情况下,您的特技的路径将反映字符集而不是单词本身.所以,如果你的整个字典都是[“坏”,“dab”,“cab”,“cable”],你的查找结构将会如下所示:
有一个时间/空间的折中方式实现这一点.在最简单(最快)的方法中,每个节点只包含单词列表,以及一个子节点[26].这可以让你定期找到你所在的孩子,只要看看孩子[s.charAt(i) – ‘a’](其中s是你的搜索字符串,我是你当前的这个特技的深度).
缺点是,你的大部分孩子阵列大部分都是空的.如果空间是一个问题,您可以使用更紧凑的表示,如链表,动态数组,哈希表等.但是,这些代价是潜在地需要在每个节点进行多个内存访问和比较,而不是简单的数组访问上面.但是,如果浪费的空间超过您的整个字典超过几兆字节,我会感到惊讶,因此基于阵列的方法可能是您最好的选择.
使用trie,您的整个置换功能将被替换为一个查找,从O(N!log D)(其中D是字典的大小,您的字符串的大小,N)到O(N log N)(因为你需要排序字符;查找本身是O(N)).
编辑:我已经把这个结构的一个(未经测试的)实现:http://pastebin.com/Qfu93E80