- // 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