TSP不是考虑这个问题的好方法。设n是文本的长度,m是查询的长度;假设n> m。幼稚溶液
best = infinity
for i = 1 to n
for j = i to n
all_found = true
for k = 1 to m
found = false
for l = i to j
if text[l] == query[k]
found = true
all_found = all_found || found
if all_found && j - i < best
best = j - i
best_i = i
best_j = j
已经多项式时间在为O(n 3 米)为界长度的话。现在让我们优化。
首先,通过散列集提升内部循环。
best = infinity
for i = 1 to n
for j = i to n
subtext_set = {}
for l = i to j
subtext_set = subtext_set union {text[l]}
all_found = true
for k = 1 to m
all_found = all_found && query[k] in subtext_set
if all_found && j - i < best
best = j - i
best_i = i
best_j = j
的运行时间是现在为O(n ),或者为O(n log n)的,如果我们使用二叉树来代替。
现在注意到当上限增加1时重新计算subtext_set
是浪费的。
best = infinity
for i = 1 to n
subtext_set = {}
for j = i to n
subtext_set = subtext_set union {text[l]}
all_found = true
for k = 1 to m
all_found = all_found && query[k] in subtext_set
if all_found && j - i < best
best = j - i
best_i = i
best_j = j
我们在为O(n 米)。现在,当subtext_set
只增加一个元素时,重新检查整个查询似乎很浪费:为什么我们不检查那一个,并记住我们必须去多少?
query_set = {}
for k = 1 to m
query_set = query_set union {query[k]}
best = infinity
for i = 1 to n
subtext_set = {}
num_found = 0
for j = i to n
if text[l] in query_set && text[l] not in subtext_set
subtext_set = subtext_set union {text[l]}
num_found += 1
if num_found == m && j - i < best
best = j - i
best_i = i
best_j = j
我们在为O(n )。到O(n)需要一些见解。首先,让我们来看看每个子多少查询词包含的例子
text = Bar has a computer at home. Bar
1 2 3 4 5 6 7
query = Bar computer a
# j 1 2 3 4 5 6 7
i +--------------
1 | 1 1 2 3 3 3 3
2 | 0 0 1 2 2 2 3
3 | 0 0 1 2 2 2 3
4 | 0 0 0 1 1 1 2
5 | 0 0 0 0 0 0 1
6 | 0 0 0 0 0 0 1
7 | 0 0 0 0 0 0 1
此矩阵具有非增柱和非减行,并普遍认为是真实的。我们希望遍历值为m的条目的底部,因为进一步对应于更长的解决方案。该算法如下。如果当前的i,j具有所有查询词,则增加i;否则,增加j。
随着我们目前的数据结构,增加j是好的,但增加我不是,因为我们的数据结构不支持删除。当一个查询词的最后一个副本消失时,我们需要保留一个多组并且减少num_found
。
best = infinity
count = hash table whose entries are zero by default
for k = 1 to m
count[query[k]] = -1
num_found = 0
i = 1
j = 0
while true
if num_found == m
if j - i < best
best = j - i
best_i = i
best_j = j
count[text[i]] -= 1
if count[text[i]] == -1
num_found -= 1
i += 1
else
j += 1
if j > n
break
if count[text[j]] == -1
num_found += 1
count[text[j]] += 1
我们已经到达O(n)。最后一个渐近相关的优化是通过仅为查询中的元素存储计数来将额外的空间使用从O(n)减少到O(m)。我会把那个作为练习。 (另外,必须更加小心地处理空查询。)