问题 检查字符串C是否是A和B的交错


给出三个字符串A,B和C.编写一个函数,检查C是否是A和B的交织。如果C包含A和B的所有字符以及个别中所有字符的顺序,则称其为交错A和B.字符串被保留。

例如:

  • 'hotdog'是'热'和'狗'的交错(简单)
  • 'superb'是'up'和'serb'的交错
  • '心痛'是'耳朵'和'疼痛'的交错
  • 'chat'是'hat'和'cat'的交错
  • '骗子'是  'chat'和'seer'交错,因为虽然它包含了每个字母的所有字母,但'seer'中的字母并没有按顺序出现

我找到了一些解决方案  但下面是我的方法可以有人告诉我,我错过了什么或 我的算法会起作用吗? 谢谢。

我的Algo:

  1. 遍历 a 和 c。在遍历时,我们首先计算两件事:char是否存在并保存索引,即 f 找到char的地方。一旦我们找到了char,我们应该在那个地方放一些特殊的char,这样我们就不会再考虑这个char了。
  2. 对于下一个char a 搜索 c 从你找到以前的char的索引,即 f。如果我们找不到回报。
  3. 和你一样 b 同样。
  4. 如果我们找到以后做了上述步骤 false 而不是重复第一次 b 比 a 并返回结果。

例如

a = xxy, b = xxz and c = xxzxxxy

从一开始:

  1. 对于a中的x,c = 0xzxxxy(我将0作为特殊字符)
  2. 对于x中的x,从索引0开始(因为我们在0处找到了前一个char)c = 00zxxxy。
  3. 对于y中的y,c = 00zxxx0
  4. 对于b中的x,c = 00z0xx0
  5. 对于b中的x,c = 00z00x0
  6. 对于b中的z,我们在索引4之后找不到z,索引4是我们找到b的前一个char的索引。

从一开始 a 返回false,所以我们将开始 b 现在。

所以从b开始:

  1. 对于b中的x,c = 0xzxxxy
  2. 对于b中的x,c = 00zxxxy
  3. 对于b中的z,c = 000xxxy
  4. 对于a中的x,c = 0000xxy
  5. 对于a中的x,c = 00000xy
  6. 对于y中的y,c = 00000x0

因此,true.cs是a和b的交错字符串。


8764
2017-09-06 18:08


起源

我怀疑这是一个比起初听起来更复杂的问题,因为你很容易最终走错路并不得不回溯。 - Hot Licks
只要它想要,这可以吗?或者您是否必须满足复杂性要求? - Cruncher
@Cruncher上面的解决方案是O(N ^ 2)。你可以在上面或你自己的算法上发表意见。 - Trying
@Trying:你为什么删除这个链接?我认为DP解决方案是您可以找到的最佳解决方案之一。 - nhahtdh
@nhahtdh你要我恢复吗?我想在这里重新尝试解决方案。 - Trying


答案:


您的解决方案代表了 贪心算法 稍加修改,因为它计算了一个字符 C 属于 A 在第一关(或 B 在第二次通过时,一旦找到匹配。这会破坏以下字符串:

A = xxyxxy
B = xxzxxz
C = xxzxxyxxyxxz

第一次传递将一个匹配的角色计为一个成员 A 将转向 C 成

00zxx0000xxz

第二个传递将匹配字符计为其成员 B 将转向 C 成

00000yxxyxx0

这是一个简单的Java实现 memoized 解:

private static boolean checkOverlap(String a, String b, String c) {
    Boolean[][][] memoize = new Boolean[a.length()+1][b.length()+1][c.length()+1];
    return checkOverlap(a, b, c, 0, 0, 0, memoize);
}
private static boolean checkOverlap(
    String a
,   String b
,   String c
,   int pa
,   int pb
,   int pc
,   Boolean[][][] memoize
) {
    Boolean res = memoize[pa][pb][pc];
    if (res != null) {
        return (boolean)res;
    }
    if (pa == a.length() && pb == b.length() && pc == c.length()) {
        res = true;
    } else if (pc == c.length()) {
        res = false;
    } else {
        res = false;
        if (pa != a.length() && c.charAt(pc) == a.charAt(pa) && checkOverlap(a, b, c, pa+1, pb, pc+1, memoize)) {
            res = true;
        } else if (pb != b.length() && c.charAt(pc) == b.charAt(pb) && checkOverlap(a, b, c, pa, pb+1, pc+1, memoize)) {
            res = true;
        }
    }
    return (memoize[pa][pb][pc] = res);
}

在ideone上演示。


9
2017-09-06 18:37



你能建议一个,可能有效率。谢谢。 - Trying
@Trying基于DP的解决方案(第二个) 来自您在编辑之前引用的站点 效率很高。您还可以通过应用将第一个解决方案(递归的解决方案)转换为有效的解决方案 记忆化。 - dasblinkenlight
我按照链接但我无法在递归方法中找到重叠的子问题,以便我可以使用记忆。请帮助我至少找到子问题,从中我可以起飞,我觉得。谢谢。 - Trying
@Trying我从你的链接添加了一个memoized递归解决方案的Java实现 - 看看。 - dasblinkenlight
@Trying我想 我们可以,但我很难证明这一点。 - dasblinkenlight


首先,我检查A的长度和B的长度相等于C的长度。

接下来,检查A的第一个字符是否等于C的第一个字符。

如果不是,检查B的第一个字符是否等于C的第一个字符。

检查以A或B开头的其他字符,具体取决于上述两个条件中的哪一个为真。

这是一个进行测试的方法:

public boolean isInterleaved(String a, String b, String c) {
    int aIndex = 0;
    int bIndex = 0;
    int cIndex = 0;
    while (cIndex < c.length()) {
        if (aIndex < a.length()) {
            if (a.charAt(aIndex) != c.charAt(cIndex)) { return false; }
            cIndex++;
            aIndex++;
        }
        if (bIndex < b.length()) { 
            if (b.charAt(bIndex) != c.charAt(cIndex)) { return false; }
            cIndex++;
            bIndex++;
        }
    }

    return true;
}

您最多可以调用此方法两次。电话会是

if (isInterleaved(a, b, c))

要么

if (isInterleaved(b, a, c))

如果A的第一个字符和B的第一个字符相等,请检查以上一步中未启动的A或B字符串开头的其他字符。

这样,您可以保存可能满足条件的字符串的复杂测试。


0
2017-09-06 18:18



谢谢你的评论。我可以做上述所有优化,但是请您告诉我上面的方法是否会给我正确的结果?谢谢。 - Trying
@Trying:我只是在C字符上做一个简单的循环。看我编辑的答案。 - Gilbert Le Blanc
我的索引超出界限: isInterleaved("xxxy", "xx", "xxxxxy"),它返回true isInterleaved("x", "xx", "x") - nhahtdh
@Trying:我忘记了A和B字符串可能有不同的长度。看我编辑的答案。在调用此方法之前,您还应该执行我概述的其他测试。 - Gilbert Le Blanc
check for:String A =“xxyxxy”;字符串B =“xxzxxz”;字符串C =“xxzxxyxxyxxz”;答案应该是真的,但你的代码将返回false。 - Trying


def isinterleave(a, b, c):
   la = len(a)
   lb = len(b)
   lc = len(c)
   if la + lb != lc: return False
   if la == lb == lc == 0: return True
   if (la > 0 and lb >0 and a[0] == b[0] == c[0]):
     return isinterleave(a[1:], b, c[1:]) or isinterleave(a, b[1:], c[1:])
   if (la > 0 and a[0] == c[0]):
     return isinterleave(a[1:], b, c[1:])
   if (lb > 0 and b[0] == c[0]):
     return isinterleave(a, b[1:], c[1:])
   return False

0
2017-09-06 19:29