16

最近,我在一次采访中被问到以下问题。

给定一个字符串 S,我需要找到另一个字符串 S2,使得 S2 是 S 的子序列,S 也是 S2+reverse(S2) 的子序列。这里的“+”表示串联。对于给定的 S,我需要输出 S2 的最小可能长度。

有人告诉我这是一个动态编程问题,但是我无法解决它。有人可以帮我解决这个问题吗?

编辑-

有没有办法在 O(N 2 ) 或更短的时间内做到这一点。

4

3 回答 3

1

这个问题有两个重要方面。

  1. 因为我们需要 S 作为 S2+reverse(S2) 的子串,所以 S2 应该有至少 n/2 的长度。
  2. 在 S2 和 reverse(S2) 连接后,有一个字母重复的模式,例如

在此处输入图像描述

所以解决方案是从 S 的中心到 S 的末端检查任何连续的元素。如果你找到一个,然后检查任一侧的元素,如图所示。

在此处输入图像描述

现在,如果您能够到达字符串的末尾,那么元素的最小数量(结果)就是从开始到找到连续元素的点的距离。在这个例子中,它的 C 即 3。

我们知道这可能不会总是发生。即您可能无法在中心找到连续的元素。假设连续元素在中心之后,那么我们可以进行相同的测试。

主弦

在此处输入图像描述

子串

在此处输入图像描述

连接字符串

在此处输入图像描述

现在到了主要的疑问。为什么我们只考虑从中心开始的左侧?答案很简单,串联的字符串由 S+reverse(S) 组成。所以我们确信子字符串中的最后一个元素在连接的字符串中是连续的。主字符串前半部分的任何重复都无法产生更好的结果,因为至少我们应该在最终连接的字符串中包含 n 个字母

现在是复杂性的问题:搜索连续字母最多给出 O(n) 现在迭代检查任一侧的元素可以给出 O(n) 的最坏情况复杂性。即最大 n/2 比较。我们可能会多次失败进行第二次检查,因此我们在复杂性之间存在乘法关系,即 O(n*n)。

我相信这是一个正确的解决方案,还没有发现任何漏洞。

于 2016-05-10T10:56:31.463 回答
0

S 中的每个字符都可以包含在 S2 中或不包含在 S2 中。有了它,我们可以构建尝试两种情况的递归:

  • S的第一个字符用于封面,
  • S 的第一个字符不用于封面,

并计算这两个封面的最小值。为了实现这一点,跟踪已经选择的 S2+reverse(S2) 覆盖了多少 S 就足够了。

有一些优化,我们知道结果是什么(找到掩体,不能有掩体),如果它不会覆盖某些东西,则不需要将第一个字符作为掩体。

简单的python实现:

cache = {}

def S2(S, to_cover):
    if not to_cover:  # Covered
        return ''
    if not S:         # Not covered
        return None
    if len(to_cover) > 2*len(S):  # Can't cover
        return None
    key = (S, to_cover)
    if key not in cache:
        without_char = S2(S[1:], to_cover)     # Calculate with first character skipped
        cache[key] = without_char
        _f = to_cover[0] == S[0]
        _l = to_cover[-1] == S[0]
        if _f or _l:
            # Calculate with first character used
            with_char = S2(S[1:], to_cover[int(_f):len(to_cover)-int(_l)])
            if with_char is not None:
                with_char = S[0] + with_char  # Append char to result
                if without_char is None or len(with_char) <= len(without_char):
                    cache[key] = with_char
    return cache[key]

s = '21211233123123213213131212122111312113221122132121221212321212112121321212121132'
c = S2(s, s)
print len(s), s
print len(c), c
于 2016-05-01T18:33:23.950 回答
0

假设 S2 是“苹果”。那么我们可以做出这样的假设:

S2 + reverseS2 >= S >= S2
"appleelppa" >= S >= "apple"

因此,给定的 S 将包括“apple”到不超过“appleelppe”的内容。它可以是“appleel”或“appleelpp”。

String S ="locomotiffitomoc";
// as you see S2 string is "locomotif" but 
// we don't know S2 yet, so it's blank
String S2 = "";
for (int a=0; a<S.length(); a++) {
    try {
        int b = 0;
        while (S.charAt(a - b) == S.charAt(a + b + 1))
            b++;
        // if this for loop breaks that means that there is a character that doesn't match the rule
        // if for loop doesn't break but throws an exception we found it.
    } catch (Exception e) {
        // if StringOutOfBoundsException is thrown this means end of the string.
        // you can check this manually of course.
        S2 = S.substring(0,a+1);
        break;
    }
}
System.out.println(S2); // will print out "locomotif"

恭喜,您找到了最低 S2。

于 2016-05-10T04:30:32.777 回答