【发布时间】:2012-03-19 17:55:57
【问题描述】:
pangrammatic window 是包含所有 26 个字母的较大文本的子字符串。引用维基百科的一个例子,给定以下文本:
我唱歌,觉得自己唱得很好;但他只是用一种非常疑惑的表情看着我的脸说,“你唱了多久了,小姐?”
文本中最小的pangrammatic窗口是这个字符串:
g 非常好;但他只是用一个非常古怪的前任看着我的脸
确实包含每个字母至少一次。
我的问题是:给定一个文本语料库,在文本中找到最小的pangrammatic 窗口最有效的算法是什么?
我已经对此进行了一些思考,并提出了以下算法。我有一种强烈的感觉,这些都不是最佳的,但我想我会把它们作为一个起点。
有一个简单的朴素算法,在时间 O(n2) 和空间 O(1) 中运行:对于字符串中的每个位置,从该位置向前扫描并跟踪你的字母见过(也许在一个位向量中,因为只有 26 个不同的字母,占用空间 O(1))。一旦你找到了所有 26 个字母,你就有了从那个给定点开始的最短 pangrammatic 窗口的长度。每次扫描可能需要 O(n) 时间,并且有 O(n) 次扫描,总共需要 O(n2) 时间。
我们也可以使用修改后的二分搜索在时间 O(n log n) 和空间 O(n) 上解决这个问题。构造 26 个数组,一个对应字母表中的每个字母,然后按排序顺序使用输入文本中每个字母的位置填充这些数组。我们可以通过简单地扫描文本来做到这一点,将每个索引附加到与当前字符对应的数组中。一旦我们有了这个,我们可以在 O(log n) 时间内找到从某个索引开始的最短 pangrammatic 窗口的长度,方法是在数组中运行 26 次二进制搜索,以找到每个字符出现在输入数组中的最早时间或在给定索引之后。无论这些数字中的哪一个最大,都会给出出现在字符串最下方的“长杆”字符,从而给出 pangrammatic 窗口的端点。运行此搜索步骤需要 O(log n) 时间,并且由于我们必须对字符串中的所有 n 个字符执行此操作,因此总运行时间为 O(n log n),其中数组的内存使用量为 O(n)。
上述方法的进一步改进是将数组和二分搜索替换为van Emde Boas trees 和前身搜索。这将创建时间增加到 O(n log log n),但将每次搜索时间减少到 O(log log n) 时间,净运行时间为 O(n log log n),空间使用量为 O(n)。
有没有更好的算法?
【问题讨论】: