从字符串'A'中删除最小字母以删除字符串'B'的所有实例

时间:2013-03-09 18:58:19

标签: string algorithm

如果我们有长度为N的字符串A和长度为M的字符串B,则其中M < N,我可以快速计算我必须从字符串A中删除的最小字母数,以便字符串B不会作为A中的子字符串出现吗?

如果我们有很小的字符串长度,这个问题很容易暴力:你只需要从0迭代一个位掩码到2^N,看看B是否作为子串发生在这里A的后续序列。但是,当N可以达到10,000并且M可以达到1,000时,该算法显然会迅速崩溃。有更快的方法吗?

示例:A = ababaa B = aba。答案= 1。删除A中的第二个a将导致abbaa,其中不包含B

编辑:用户n.m.发布了一个很好的反例:aabccabc。我们要删除单个b,因为删除任何ac都会创建字符串abc的另一个实例。

6 个答案:

答案 0 :(得分:2)

通过动态编程解决它。设dp [i] [j]使A [0 ... i-1]的后缀为B [0 ... j-1]以及A [0 ... i] don'的最小运算符t包含B,dp[i][j] = Infinite索引运算符是不可能的。然后

if(A[i-1]=B[i-1]) 
   dp[i][j] = min(dp[i-1][j-1], dp[i-1][j])    
else dp[i][j]=dp[i-1][j]`,

return min(A[N][0],A[N][1],...,A[N][M-1]);`

答案 1 :(得分:0)

您可以对字符串A进行图表搜索。对于大N和特殊输入,这可能太慢了,但它应该比指数强力算法更好。也许是BFS。

答案 2 :(得分:0)

我不确定这个问题是否还有兴趣,但我有一个想法可能会有用。

一旦我们确定问题不是找到子字符串,就是决定哪个字母更方便从字符串A中删除,我的解决方案看起来很简单:如果你发现B字符串出现在A中,你能做的最好的事情就是删除一个字符串内的字符,关闭到右边的键盘...让我们说一个最后一个。这就是为什么如果你有一个实际结束它开始的子字符串的原因,如果你在开头删除一个字符,你只需删除其中一个B出现,而你实际上可以一次删除两个。

伪cose中的算法:

String A, B;
int occ_posit = 0;
N = B.length();

occ_posit = A.getOccurrencePosition(B); // pseudo function that get the first occurence of B into A and returns the offset (1° byte = 1), or 0 if no occurence present.

while (occ_posit > 0)  // while there are B into A
{
    if (B.firstchar == B.lastchar)  // if B starts as it ends
    {
        if (A.charat[occ_posit] == A.charat[occ_posit+1])
            A.remove[occ_posit - 1]; // no reason to remove A[occ_posit] here
        else
            A.remove[occ_posit]; // here we remove the last char, so we could remove 2 occurencies at the same time
    }
    else
    {
        int x = occ_posit + N - 1;
        while (A.charat[x + 1] == A.charat[x])
            x--; // find the first char different from the last one

        A.remove[x]; // B does not ends as it starts, so if there are  overlapping instances they overlap by more than one char. Removing the first that is not equal to the char following B instance, we kill both occurrencies at once.
    }
}

让我们用一个例子来解释:

A =“123456789000987654321” B =“890”

以表格形式阅读:

occ_posit:  123456789012345678901

       A = "123456789000987654321"
       B =        "890"

第一次出现在occ_posit = 8.B在它开始时没有结束,所以它进入第二个循环:

int x = 8 + 3 - 1 = 10;
while (A.charat[x + 1] == A.charat[x])
    x--; // find the first char different from the last one
A.remove[x];

同时发现A.charat11匹配A.charat [10](=“0”),因此x变为9然后在退出时为A.charat [10]与A.charat9不匹配。然后成为:

A =“12345678000987654321”

没有更多的出现。

让我们尝试另一个: A =“abccccccccc” B =“abc”

第一次出现在occ_posit = 1.B在它开始时没有结束,所以它进入第二个循环:

int x = 1 + 3 - 1 = 3;
while (A.charat[x + 1] == A.charat[x])
    x--; // find the first char different from the last one
A.remove[x];

同时发现A.charat4匹配A.charat [3](=“c”),因此x变为2然后在退出时为A.charat [3]与A.charat2不匹配。然后成为:

A =“accccccccc”

让我们尝试重叠:

A =“abcdabcdabff” B =“abcdab”

算法导致:A =“abcdacdabff”没有更多的出现。

最后,一个字母重叠:

A =“abbabbabbabba” B =“abba”

B在开始时结束,因此它进入第一个if:

if (A.charat[occ_posit] == A.charat[occ_posit+1])
            A.remove[occ_posit - 1]; // no reason to remove A[occ_posit] here
        else
            A.remove[occ_posit]; // here we remove the last char, so we could remove 2 occurencies at the same time

允许删除B实例的最后一个“a”。所以:

步骤1步:A =“abbbbabbabba” 2°步骤:A =“abbbbabbbba”,我们完成了。

希望这有帮助

编辑:请注意,当您在搜索时接近A端时,必须稍微纠正algotirhm以防止出错,但这只是一个简单的编程问题。

答案 3 :(得分:0)

这是我想出的草图。

首先,如果A包含在B中找不到的任何符号,则将A拆分为一堆较小的字符串,其中只包含B中找到的那些字符。将算法应用于每个较小的字符串,然后将它们粘合在一起得到总结果。这确实可以作为优化。

接下来,检查A是否包含B中的任何一个。如果没有,则表示您已完成。如果A = B,则删除所有这些。

我认为相对贪婪的算法可能有用。

首先,在A中标记属于至少一次出现的B的所有符号。设A = aabcbccabcaa,B = abc。粗体表示这些标记的字符:

abc bcc abc aa。如果有重叠,请标记所有可能的。这个操作是天真的近似(A-B)操作,但我相信它可以在(A / B)操作中完成。

考虑删除A中的每个标记字母:a abc bcc abc aa。

检查删除标记的字母是否会减少标记字母的数量。您只需要检查可能受删除字母影响的子字符串。如果B的长度为4,则只有在检查x时才需要删除从以下位置开始的子串:

-------x------
    ^^^^

无论是否存在x,都会存在任何进一步的左或右。

例如:

在以下字符串中标记[a]:a [a] bc bcc abc aa。

它的删除产生abcbccabcaa,当标记时产生 abc bcc abc aa,其具有相同数量的标记字符。由于此操作仅需要相对数量,因此对于每个所选字母,可以在大约2B时间内完成。对于每个,分配两者之间的相对差异。选择一个最大的任意一个并删除它。重复直到完成。每次通过大约最多2AB次操作,最多A次通过,总时间约为2A ^ 2 B.

在上面的示例中,分配了这些值:

aabcbccabcaa
 033   333

所以随意删除第一个标记的b给你:aacbccabcaa。如果你重复这个过程,你会得到:

aacbccabcaa
      333  

最终结果已经完成。

我相信算法是正确的最小化。我认为,只要A只需要一次删除,算法必须才是最佳的。在这种情况下,减少最可能的匹配(即:所有这些)的字母应该是最好的。不过,我无法提出这样的证据。我有兴趣找到最优的反例。

答案 4 :(得分:0)

查找主字符串中每个子字符串的indeces。

然后使用动态编程算法(所以记住中间值),从主字符串中删除属于子字符串一部分的每个字母,将1加到计数中,然后重复。

你可以找到这些字母,因为它们在每个匹配索引的缺口+ B的长度内。

A = ababaa
B = aba
count = 0

indeces = (0, 2)

A = babaa, aabaa, abbaa, abbaa, abaaa, ababa
B = aba
count = 1

(2nd abbaa is memoized)

indeces = (1), (1), (), (), (0), (0, 2)

answer = 1

您可以更进一步,并尝试记住子字符串的子字符串匹配,但这可能实际上不是性能提升。

不确定确切的界限,但不应该花费太长时间计算。

答案 5 :(得分:-1)

好的,我想我有一个简单的解决方案 我要假装A,B只是二进制数字串。

A= 011010101001001101
B= 010

在A上应用XNOR过滤器,结果为B.结果将为 C 假设B的原点位于其中心 (这意味着在A中查找B。如果找到匹配项,请将1 in C放入该位置。

A= 011010101001001101
C= 000010101001000000

简单地说,C显示A包含B的位置 例如,您可以删除C所示位置中的所有字符,这样可以解决您的问题。但它不是最佳解决方案。

但找到最佳解决方案很容易 好的,现在问题更简单了。我可以解释一下这些算法问题:

  • C显示了绘画的位置。每个防护装置可以保护一系列X(X =长度(B)/ 2 + 1)。放置最少数量的防护装置,以保护所有画作。
  • 从C中删除最小数量的1。对于每1个被移除的,也删除了lenth(B)/ 2 + 1附近的所有1。

这不是新问题,现有解决方案。应用一个这样的解决方案。
这将产生以下结果。 D 显示已删除1的位置:

C= 000010101001000000
D= 000000100001000000

计算D中的1的数量 - > 2.这意味着您可以从A中删除2个元素,因此B不是A的子字符串。