【发布时间】:2012-03-13 04:02:54
【问题描述】:
我正在尝试查找给定字符串中的所有子字符串。对于像rymis 这样的随机字符串,子序列将是[i, is, m, mi, mis, r, ry, rym, rymi, rymis, s, y, ym, ymi, ymis]。从Wikipedia 开始,长度为n 的字符串将有n * (n + 1) / 2 的子字符串总数。
可以通过以下sn-p的代码找到:
final Set<String> substring_set = new TreeSet<String>();
final String text = "rymis";
for(int iter = 0; iter < text.length(); iter++)
{
for(int ator = 1; ator <= text.length() - iter; ator++)
{
substring_set.add(text.substring(iter, iter + ator));
}
}
这适用于较小的字符串长度,但由于算法接近O(n^2),因此对于较大的长度显然会减慢。
还阅读了可以在O(n) 中进行插入的后缀树,并注意到可以通过从右侧删除 1 个字符直到字符串为空来重复插入子字符串来获得相同的子序列。应该是关于O(1 + … + (n-1) + n) 这是一个summation of n -> n(n+1)/2 -> (n^2 + n)/ 2,这又是在O(n^2) 附近。尽管似乎有一些后缀树可以在log2(n) 时间进行插入,这将是O(n log2(n)) 更好的一个因素。
在我深入研究 Suffix Trees 之前,这是否是正确的路线,是否有其他算法对此更有效,或者O(n^2) 是否与此一样好?
【问题讨论】:
-
由于该集合包含 n * (n + 1) / 2 个值,因此您必须对集合执行 n * (n + 1) / 2 次插入,所以我不知道如何该算法可能小于 O(n^2)。
-
@JBNizet - 我同意,没有办法避免触及每个子字符串元素。由于原始集合的大小为n,并且大约有n^2个元素要访问,因此这很可能无法提高效率。
-
这不是家庭作业。使用发布的其他两种算法它们都比我原来的慢,但我注意到数据结构可能不是最优的。如果算法已经产生了唯一的子字符串,那么不需要 TreeSet(数据结构可以稍后排序),并且动态数组也会因为插入量大而效率低下(需要扩展其内部数组并复制)。
-
通过一些测试,所有三种算法都会生成正确的答案。我在原始帖子中的算法在经验上是最快的,因为它具有较少的恒定时间成本,但并没有显着差异。当像
aba这样添加带有重复字符的字符串时,问题变得更加复杂,其中子字符串开始变得重复,那么就不能再保证结构只包含唯一元素。如果可以确保它确实如此,那么像LinkedList这样的数据结构将比*Set或ArrayList更快地提高速度。 -
@ntin - 重复应该不是问题,因为您总是可以比当前瓶颈更快地删除它们。对数组进行堆排序,然后遍历它,如果前一项相同,则删除当前项。这两个操作应该分别是O(n log n)和O(n)。