寻找最小pangrammatic窗口的有效算法?

Posted

技术标签:

【中文标题】寻找最小pangrammatic窗口的有效算法?【英文标题】:An efficient algorithm for finding smallest pangrammatic windows? 【发布时间】: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)。


有没有更好的算法?

【问题讨论】:

【参考方案1】:

对于每个字母,请跟踪最近的目击事件。每当您处理一个字母时,更新相应的目击索引并计算所有字母的目击索引的范围(最大-最小)。找到范围最小的位置。

复杂度 O(n)。 O(nlog(m)) 如果你考虑字母大小 m。

【讨论】:

+1 在发布问题大约五分钟后,我意识到这个解决方案是可能的。如果您制作端点的 vEB 树,您实际上可以将任意字母 m 设为 O(m + n log log m)。很好的答案! @ElKamina,我在以下输入中尝试了你的算法,它没有返回正确的答案。如果我做得不对,有人可以解释一下。 Alphabate: a,b,c Input string : aabbabcca Sighting index:a-:8,b-:5,c-:7 Range(min,max): (5,7) , Ans : bcca, but the correct ans should是“abc” @Prafulla 这是最近的目击事件。处理完 7 后,字母看起来像 (5,6,7)(分别代表 a,b,c),处理完第 8 个 (5,6,8), 9: (9,6,8)。 @ElKamina 很抱歉,我还是不明白。从(9,6,8),我们计算出窗口为(6-9),即“bcca”,但最小的窗口是“abc”? @Prafulla 您应该跟踪窗口及其长度。在这种情况下,有三个可能的窗口 7:(5,6,7), 8:(5,6,8), 9:(9,6,8) 。它们的窗口长度分别为 3、4、4。所以最佳解决方案是 7:(5,6,7),正如你所提到的,它是 'abc'【参考方案2】:

这个算法有O(M)空间复杂度和O(N)时间复杂度(时间不取决于字母大小M):

    推进第一个迭代器并为每个已处理的字母增加计数器。当所有 26 个计数器都不为零时停止。 为每个已处理的字母增加第二个迭代器并减少计数器。当这些计数器中的任何一个为零时停止。 使用迭代器之间的差异更新迄今为止的最佳结果并继续执行步骤 1。

如果存储字符串中的位置而不是字符计数器,则该算法可能会有所改进。在这种情况下,第 2 步应该只读取这些位置并与当前位置进行比较,第 1 步应该更新这些位置并(大部分时间)搜索文本中的某些字符。

【讨论】:

我很确定这行得通,但我不确定我明白为什么它不会以某种方式意外跳过窗口。您确定这会正确考虑所有窗口吗? @templatetypedef,证明很简单。步骤 2 的不变性是,从第二个迭代器开始的最短 pangrammatic 窗口的长度正好是(第一个迭代器 - 第二个迭代器),因为递减第一个迭代器会从集合中删除一个字符。因此,您可以将此算法视为 n^2 算法的优化变体。 这个 O(N) 怎么算,怎么不依赖字母表大小 M?具体来说,如何检查“当所有 26 个计数器均非零时停止”。在 O(1) 中,(因为它是常数,所以可以在 O(1) 中完成,但对于 M 的一般情况?) @kolistivra:需要一个额外的计数器,每当 M 个计数器中的一些变为非零时,该计数器就会增加。这个单个计数器可以在 O(1) 时间内与 M 进行比较。至于整体复杂度,每个迭代器都提前了 O(N) 次,每次迭代需要 O(1) 时间来完成,所以这个算法只需要 O(N) 时间。 (实际上是 O(N+M) 因为我们需要初始化 M 个计数器,但是 O(N+M) = O(N) 因为 M

以上是关于寻找最小pangrammatic窗口的有效算法?的主要内容,如果未能解决你的问题,请参考以下文章

滑动窗口算法

滑动窗口算法

如何在磁盘调度算法中找到平均寻道时间?

openharmony 军棋工兵寻径算法的实现

最近的一组3分

寻找最小瓶颈路径的线性时间算法