如何从Trie中检索给定长度的随机单词

Dev*_*Zot 8 java algorithm tree trie data-structures

我有一个简单的Trie,我用来存储大约80k字长2到15的字.它非常适合检查字符串是否为单词; 但是,现在我需要一种获取给定长度的随机单词的方法.换句话说,我需要"getRandomWord(5)"来返回一个5个字母的单词,所有5个字母的单词都有相同的返回机会.

我能想到的唯一方法是选择一个随机数并遍历树的广度优先,直到我通过了那么多所需长度的单词.有一个更好的方法吗?

可能没必要,但这是我的特里的代码.

class TrieNode {
    private TrieNode[] c;
    private Boolean end = false;

    public TrieNode() {
        c = new TrieNode[26]; 
    }

    protected void insert(String word) {
        int n = word.charAt(0) - 'A';
        if (c[n] == null)
            c[n] = new TrieNode();
        if (word.length() > 1) {
            c[n].insert(word.substring(1));
        } else {
            c[n].end = true;
        }
    }

    public Boolean isThisAWord(String word) {
        if (word.length() == 0)
            return false;
        int n = word.charAt(0) - 'A';
        if (c[n] != null && word.length() > 1)
            return c[n].isThisAWord(word.substring(1));
        else if (c[n] != null && c[n].end && word.length() == 1)
            return true;
        else
            return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:标记的答案效果很好; 我会在这里为后代添加我的实现,以防它可以帮助任何有类似问题的人.

首先,我创建了一个帮助程序类来保存我在搜索中使用的TrieNodes的元数据:

class TrieBranch {
    TrieNode node;
    int letter;
    int depth;
    public TrieBranch(TrieNode n, int l, int d) {
        letter = l; node = n; depth = d;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是包含Trie并实现随机单词搜索的类.我是一个初学者,所以可能有更好的方法来做到这一点,但我测试了一点,它似乎工作.没有错误处理,所以请注意.

class Dict {

    final static int maxWordLength = 13;    
    final static int lettersInAlphabet = 26;
    TrieNode trie;
    int lengthFrequencyByLetter[][];
    int totalLengthFrequency[];

    public Dict() {
        trie = new TrieNode();
        lengthFrequencyByLetter = new int[lettersInAlphabet][maxWordLength + 1];
        totalLengthFrequency = new int[maxWordLength + 1];
    }

    public String getRandomWord(int length) {
        // Returns a random word of the specified length from the trie
        // First, pick a random number from 0 to [number of words with this length]
        Random r = new Random();
        int wordIndex = r.nextInt(totalLengthFrequency[length]);

        // figure out what the first letter of this word would be
        int firstLetter = -1, totalSoFar = 0;
        while (totalSoFar <= wordIndex) {
            firstLetter++;
            totalSoFar += lengthFrequencyByLetter[firstLetter][length];
        }
        wordIndex -= (totalSoFar - lengthFrequencyByLetter[firstLetter][length]);

        // traverse the (firstLetter)'th node of trie depth-first to find the word (wordIndex)'th word
        int[] result = new int[length + 1];
        Stack<TrieBranch> stack = new Stack<TrieBranch>();
        stack.push(new TrieBranch(trie.getBranch(firstLetter), firstLetter, 1));
        while (!stack.isEmpty()) {
            TrieBranch n = stack.pop();
            result[n.depth] = n.letter;

            // examine the current node
            if (n.depth == length && n.node.isEnd()) {
                wordIndex--;
                if (wordIndex < 0) {
                    // search is over
                    String sResult = "";
                    for (int i = 1; i <= length; i++) {
                        sResult += (char)(result[i] + 'a');
                    }
                    return sResult;
                }
            }

            // handle child nodes unless they're deeper than target length
            if (n.depth < length) {
                for (int i = 25; i >= 0; i--) {
                    if (n.node.getBranch(i) != null)
                        stack.push(new TrieBranch(n.node.getBranch(i), i, n.depth + 1));
                }
            }
        }
        return "failure of some sort";
    }
}
Run Code Online (Sandbox Code Playgroud)

使用一个随意字典(80k字最大长度为12),每次调用getRandomWord()需要重复.2ms,并使用更彻底的字典(250K字,最大长度为24),每次调用大约需要1ms.

Joh*_*ohn 7

为了确保你有机会获得每个5个字母的单词,你需要知道你的树中有多少个5个字母的单词.因此,在构建树时,可以将要添加的单词的长度添加到两个计数器:整个频率计数器和一个字母频率计数器:

int lengthFrequencyByLetter[letterIndex][maxWordLength-1]
int totalLengthFrequency[maxWordLength-1]
Run Code Online (Sandbox Code Playgroud)

所以如果你有4000个5个字母的单词,其中213个以"d"开头,那么

lengthFrequencyByLetter[3][4] = 213
Run Code Online (Sandbox Code Playgroud)

totalLengthFrequency[4] = 4000
Run Code Online (Sandbox Code Playgroud)

完成后,将所有内容添加到树中.(字母"a"为0,"b"为1,......"z"为25.)

从这里,您可以搜索n给定的单词length,其中n是从均匀随机分布中选取的随机整数,范围为(0,totalLengthFrequency[length-1]).

假设您的结构中有4000个5个字母的单词.你选择随机数1234.现在你可以检查

lengthFrequencyByLetter[0][4]
lengthFrequencyByLetter[1][4]
lengthFrequencyByLetter[2][4]
lengthFrequencyByLetter[3][4]
Run Code Online (Sandbox Code Playgroud)

反过来,直到你总共超过1234.然后你很快就知道第1234个5个字母单词的起始字母是什么,然后在那里搜索.您不必每次都从头开始搜索树中的每个单词.