String 搜索满足某些条件的最小字符串

String 搜索满足某些条件的最小字符串,string,dynamic-programming,subsequence,String,Dynamic Programming,Subsequence,最近,我在一次采访中被问到以下问题 给定一个字符串S,我需要找到另一个字符串S2,使得S2是S的子序列,并且S是S2+reverse(S2)的子序列。这里的“+”表示串联。我需要输出给定S的S2的最小可能长度 我被告知这是一个动态规划问题,但我无法解决它。有人能帮我解决这个问题吗 编辑- 有没有办法在O(N2)或更小的范围内实现这一点。S中的每个字符都可以包含在S2中,也可以不包含在S2中。利用这一点,我们可以构造尝试两种情况的递归: S的第一个字符用作封面 S的第一个字符不是 用作掩护 并

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

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

我被告知这是一个动态规划问题,但我无法解决它。有人能帮我解决这个问题吗

编辑-


有没有办法在O(N2)或更小的范围内实现这一点。

S中的每个字符都可以包含在S2中,也可以不包含在S2中。利用这一点,我们可以构造尝试两种情况的递归:

  • S的第一个字符用作封面
  • S的第一个字符不是 用作掩护
并计算这两个覆盖的最小值。为了实现这一点,跟踪已经选择的S2+反转(S2)覆盖了多少S就足够了

有一些优化,我们知道结果是什么(找到了cover,不能有cover),如果第一个字符不能覆盖某些内容,则不需要将其作为cover

简单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
cache={}
def S2(S,至外壳):
如果不覆盖:#覆盖
返回“”
如果不是S:#未涵盖
一无所获
如果len(to_cover)>2*len(S):#无法覆盖
一无所获
键=(S,至盖)
如果密钥不在缓存中:
不带_char=S2(S[1:],到_cover)#跳过第一个字符进行计算
缓存[键]=不带字符
_f=覆盖率[0]==S[0]
_l=to_覆盖[-1]==S[0]
如果_f或_l:
#使用第一个字符进行计算
当字符=S2(S[1:],to_cover[int(_f):len(to cover)-int(_l)])
如果with_char不是None:
with_char=S[0]+with_char#将char追加到结果
如果不带字符是None或len(带字符)让我们说S2是“apple”。那么我们可以做出这样的假设:

S2+反转2=S=S2
“苹果LPPA”=S=“苹果”

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

String S=“motionffitomic”;
//正如您所看到的,S2字符串是“momotif”,但是
//我们还不知道S2,所以它是空白的
字符串S2=“”;

对于(inta=0;a,这个问题有两个重要方面

  • 因为我们需要S作为S2+反向(S2)的子串,S2应该有 至少n/2长度
  • 在S2和反向(S2)连接之后,有一种模式 字母表重复出现,例如
  • 因此,解决方案是检查从S的中心到S的末端是否有任何连续的元素。如果找到一个元素,则检查任意一侧的元素,如图所示

    现在,如果您能够到达字符串的末尾,那么最小元素数(result)是从开始到找到连续元素的点的距离。在本例中是C i.e 3

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

    主串

    子串

    串联字符串

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

    现在,复杂的问题是: 搜索连续字母表的最大值为O(n) 现在,迭代地检查两边的元素可以得到最坏情况下的复杂性O(n),即最大n/2比较。 第二次检查可能会失败很多次,因此复杂度之间存在乘法关系,即O(n*n)


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

    Ante-我无法理解您的解决方案。您是否将字符串作为DP的参数传递?DP的状态是什么?抱歉,我从未使用过python,所以这段代码让我感到困惑。@LTim两个方法参数都是字符串。第一个参数是初始字符串的“尾部”,从选择S2的剩余部分。第二个参数是S的剩余部分将被S2+反向(S2)覆盖。如果找到S2,方法将返回字符串S2。如果找不到S2,则方法将返回None。您的解决方案似乎效率低下,是否有办法在O(N^2)中实现这一点或更少。我不需要字符串,只需要最小长度。@LTim将覆盖长度更改行“with_char=S[0]+with_char”更改为“with_char=1+with_char”。缓存中的最大元素数为N^3,因为参数S可以有N个不同的值,并且覆盖N*(N-1)/2个值。我认为实际的数字要小得多,因为调用的顺序和to_cover的结构,大约是N^2。缓存可以被索引以具有恒定的访问。因此,我认为运行时间是O(N^2)其中N是S长度。上面的示例在我的笔记本电脑上运行约23ms。如果字符串的不同字符数多于,则重复次数较少,并且运行速度更快。Hi@Ante:在
    abacaba
    的情况下,您的解决方案似乎产生了错误的结果。它返回
    abcabca
    ,但最小的解决方案是
    abacaba
    。是否为O(N^3)解决方案可以接受吗?不,我需要一个比O(n^3)更好的解决方案。代码呢?子字符串≠ 子序列
    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"