【问题标题】:What are efficient ways to delete recursively all occurrences of a substring in a string? [closed]递归删除字符串中所有出现的子字符串的有效方法是什么? [关闭]
【发布时间】:2015-02-23 03:51:18
【问题描述】:

对不起,我的英语不好。

我有这样的问题。给定一个字符串(可能是一个很长的字符串)和一个模式,如何删除字符串中所有出现的模式?请注意,删除前一个后可能会出现新的事件。例如:

string: deaabcbcef
pattern: abc
result: deef

是否有任何算法或有效的方法来解决它(在几秒钟内)?

P/S:我举了一个简单的例子来说明清楚,但我认为不是这样。字符串长度可以达到 10^6 个字符,时间限制为 2 秒。有什么算法吗?

【问题讨论】:

  • 几秒钟内?对于字符串“deaabcbcef”,您可以在几微秒内以幼稚的方式完成...
  • 请注意,从左到右的删除不同于从右到左的删除。例如,字符串:abcdcdc,模式:cdc,您可以得到结果为 'abdc'(从左到右)或 'abcd'(从右到左)。

标签: string algorithm


【解决方案1】:

对于python,不知道有没有比简单的老式循环更好的方法:

new = old.replace(pattern, '')
while new != old:
    old = new
    new = old.replace(pattern, '')

示例:

>>> old = 'deaabcbcef'
>>> pattern = 'abc'
>>> new = old.replace(pattern, '')
>>> while new != old:
...     old = new
...     new = old.replace(pattern, '')
... 
>>> new
'deef'

【讨论】:

  • 如果您在出现之前有千兆字节的前缀,最好保存索引以开始查找。我不知道“可能是一个很长的字符串”是什么意思,但是反复搜索你知道不包含任何事件的东西可能会很浪费。
  • O(n**2)算法。 old='a'*n+'b'*n, pattern='ab'n=10**6 可能需要一个多小时。
  • @J.F.Sebastian -- 诚然,这里有病态的坏情况......在大多数正常情况下,我希望只迭代一次或两次。如果有更多的病态病例,我可能会使用bytearray,以便“字符串”可以在适当的位置发生变异——但在编码之前(这会是一团糟),我需要知道值得我花时间:-)
【解决方案2】:

让一个字符串H(对于Haystack),我们希望在其中递归地擦除字符串N的每一次出现(对于)。我将描述的过程执行模式的从左到右删除。

主要思想

要做的第一件事是在H中找到每个不重叠的N。您可以使用 Z-algorithm 在 O(|N| + |H|) 中执行此操作。让 S1,..., Sk 这些出现。

最初,每个 Si 都有一个起始位置(它在原始字符串中的索引)和一个结束位置。我们将跟踪这些职位。让 b(i)e(i) 成为这些位置。

删除 Si 可能会产生另一个针线匹配。只有在之前有一些字符时才可以这样做。这里,我们只对窗口(1)感兴趣:

W(i) = L(i) U R(i),其中:
L(i ) = [e(i) - |N| + 1, e(i)-1]
R(i) = [s(i)+1, s(i) + |N| - 1]

使用 Z 算法的计算值,我们可以探索窗口的“左侧”部分。

Z 属性: Z 值 在位置 e(i) - k 必须是至少 k em> 匹配存在。

因此,如果左侧窗口中没有满足此标准的索引,我们就完成了 Si。否则,我们为 W(i) 构建一个新的 Z-arrayW(i) 中的 N 只能有一个非重叠匹配。我们将永远选择“最左边”

这场比赛将有一个新的开始位置b(i)(在L(i)中)和一个更新的结束位置e(i) em> 在 R(i) 中。类似地,我们更新 L(i)R(i) 以使用 (1) 中的等式与新的 b(i)e(i)。递归一直持续到 L(i) 中没有字符或 L(i) 中没有字符满足 Z 属性

这个想法是使用上面显示的递归方法处理每个 Si。我们将从最左匹配(i = 0)到最右处理它们。

请注意,给定匹配 i 的左侧窗口可能会与其他匹配的某些已删除字符重叠

处理完每个 Si 后,您将拥有一组定义已删除字符的窗口。您以这种方式处理 W(i)

  1. s = 0, e = |H| - 1
  2. 对于 i=0..nmatch - 1
    1. e = L(i)[|L(i)|-1]
    2. 复制H[s..e]O
    3. s = R(i)[0], e = |H| - 1
  3. 复制H[s..e]

经过此过程,O 就是您要查找的输出字符串!

解决“擦除索引”问题

在处理任何 i > 1Si 时,定义窗口可能会出现问题。实际上,e(i) - k 可能落在给定 k 的“已擦除”索引上。

要解决这个问题,您可以在窗口和原始字符串中的 实际 位置之间构建一个映射数组。要计算这些值,您可能需要遍历 Sjj 的窗口。

最初,设置 L(i)[k] = e(i) - |N| + 1 + k 对于 k = 0..|N|-2
对于给定的索引 m 使得 L(i)[m] = t = e(i) - k:

而 (i > 0 和 t )

  1. 如果 δ = t - R(i-1)[0] :
    1. 如果 |δ| > |L(i-1)|:
      1. δ = δ - |L(i-1)|t = L(i-1)[0] - δ
      2. i = i-1,继续下一个循环迭代
    2. 否则,让 L(i)[m] = L(i-1)[|L(i-1) + δ] 并退出
  2. 其他退出

如果 t 为负数,则该字符不存在。此外,我们现在 abs(t) - 1 下一个字符也不存在,因此可以继续 m + abs(t ) 而不是 m + 1。最后,我们只保留窗口中已有的字符。

同样,如果δ ,那么我们知道下一个|δ| - 2 个字符的索引范围为 L(i)[|L(i)| + δ + 1]L(i)[|L(i)| -1].

复杂性分析

Z 算法的初始运行时间为O(|H| + |N|)。每个匹配的处理包括窗口的维护和对后续模式匹配的实际搜索。

搜索是O(|N|),因为窗口定义了最多2.|N|-2个字符的字符串.

windows 的维护有点难以分析。但如果我们使用我之前的评论跳过不必要的计算,最后应该最多 nmatch 。|N|

最后,您的上限为 O(|H| + nmatch².|N|)

【讨论】:

  • 感谢您的详细解释。我会试试这个。无论如何,如果您给我一些代码,我将不胜感激。不过非常感谢。
  • @user1680791 我看不到您在问题中要求代码的位置。此外,甚至没有语言标签...
【解决方案3】:

Python:

代码:

import string
input="salsa"
output = string.replace(input,"sa", "re")
print output

输出:

相对

【讨论】:

    猜你喜欢
    • 2015-04-22
    • 2014-08-07
    • 2011-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-16
    • 2015-11-11
    相关资源
    最近更新 更多