如果我们有N个单词,每个单词的最大长度为L,那么你的算法将花费O(N*L^3)的时间(假设添加到trie中是与添加单词的长度成线性关系)。然而,生成的trie的大小(节点数)最多为O(N*L^2),所以看起来你浪费了时间,可以做得更好。
实际上,你确实可以做到更好,但需要从袖子里拿出一些技巧。此外,你将不再需要trie。
1.常数时间内使用.substring()
在Java 7中,每个字符串都有一个后备的char[]数组,以及起始位置和长度。这使得.substring()方法可以在常数时间内运行,因为String是一个不可变类。创建一个新的String对象,只是用不同的起始位置和长度,但是共用相同的char[]数组。
你需要稍微扩展一下这个方法,在字符串末尾添加字符。总是创建一个新的字符串对象,但保持相同的char[]数组。
在追加单个字符后,在常数时间内重新计算哈希值。
再次,让我使用Java中的String的hashCode()函数:
int hash = 0;
for (int i = 0; i < data.length; i++) {
hash = 31 * hash + data[i];
}
现在,如果在单词末尾添加一个字符,哈希会如何改变?很简单,只需添加它的值(ASCII代码)乘以
31^length
。您可以将31的幂保留在某个单独的表中,也可以使用其他质数。
使用技巧1和2,您可以在时间
O(N*L^2)
内生成所有子字符串,这是子字符串的总数。只需始终从长度为一的字符串开始,每次添加一个字符。将所有字符串放入单个HashMap中,以减少重复。
(您可以跳过2和3,在排序时/之后丢弃重复项,也许速度会更快。)
对子字符串进行排序,然后您就可以开始了。
嗯,当我到达第4点时,我意识到我的计划行不通,因为在排序时需要比较字符串,这可能需要
O(L)
的时间。我想出了几种解决方法,其中包括桶排序,但没有一种比原始的
O(N*L^3)
更快。
我会把这个答案放在这里,以防它能激励到某人。
如果您不知道
Aho-Corasic算法,可以了解一下,它可能对您的问题有所帮助。