Algorithm 字符串中回文子序列的总数

Algorithm 字符串中回文子序列的总数,algorithm,data-structures,dynamic-programming,Algorithm,Data Structures,Dynamic Programming,问题是这样的-- 对于作为输入的每个字符串,您需要告诉它的回文子序列的数量(不一定是不同的)。请注意,空字符串不是回文。 例如,“aab”的回文子序列是: “a”、“a”、“b”、“aa”,该方法返回4 我想到了寻找最长回文子序列的动态规划解决方案,因此试图从中吸取一些想法。无法真正获得解决方案。可能甚至不需要动态规划。请给我一些建议 还有一个陷阱。当条件“不必是不同的”被删除时,我们是否仍然可以在不实际生成所有回文子序列的情况下进行计数?下面是一个可怕的O(n^4)解决方案: 每个回文子序列从

问题是这样的--

对于作为输入的每个字符串,您需要告诉它的回文子序列的数量(不一定是不同的)。请注意,空字符串不是回文。 例如,“aab”的回文子序列是:

“a”、“a”、“b”、“aa”,该方法返回4

我想到了寻找最长回文子序列的动态规划解决方案,因此试图从中吸取一些想法。无法真正获得解决方案。可能甚至不需要动态规划。请给我一些建议

还有一个陷阱。当条件“不必是不同的”被删除时,我们是否仍然可以在不实际生成所有回文子序列的情况下进行计数?

下面是一个可怕的O(n^4)解决方案:

每个回文子序列从某个位置i开始,在某个位置j>=i结束,这样x[i]=x[j],其“内部”(除第一个和最后一个字符外的所有字符)要么为空,要么为x[i+1..j-1]的回文子序列

所以我们可以定义f(i,j)为从i开始到j>=i结束的回文子序列的数量。然后

f(i, j) = 0 if x[i] != x[j]
f(i, i) = 1 (i.e. when j = i)
f(i, j) = 1 + the sum of f(i', j') over all i < i' <= j' < j otherwise
f(i,j)=0如果x[i]!=x[j]
f(i,i)=1(即当j=i时)

f(i,j)=1+整个i[编辑:2015年10月19日:一位匿名评论员指出了公式的一个问题,这促使我注意到另一个更大的错误…现在已修复。]

现在我了解了如何将求解时间降低到O(n^2)。我会留下我的另一个答案,以防万一这是一个有趣的垫脚石。注:这只是问题的第一部分的解决方案;我认为没有办法有效地只计算不同的回文子序列(PS)

与其计算从位置i和j开始和结束的PS的数量,不如让我们计算从i开始或之后到j结束或之前的PS的数量。称之为g(i,j)

当j>i时,我们可以试着写出g(i,j-1)+g(i+1,j)+(x[i]==x[j])*g(i+1,j-1)。但这不太管用,因为前两个术语将加倍计算任何在i之后开始,在j之前结束的PS

关键是要注意,我们可以通过减去g()的其他值,或者再加上更多的g()值来补偿重复计算,很容易计算出在某个精确位置开始或结束的PS数。例如,从i开始到j结束的PS的数量是g(i,j)-g(i+1,j)-g(i,j-1)+g(i+1,j-1):最后一项修正了这样一个事实,即第二项和第三项都计算了在i之后开始到j之前结束的所有g(i+1,j-1)PS

从i开始或之后到j结束或之前的每一个PS正好属于4类中的1类:

  • 它在i之后开始,在j之前结束
  • 它从i开始,在j之前结束
  • 它在i之后开始,在j结束
  • 它从i开始,到j结束
  • g(i+1,j)统计类别1或3中的所有p,g(i,j-1)统计类别1或2中的所有p,因此它们的总和g(i+1,j)+g(i,j-1)分别统计类别2或3中的所有p一次,以及类别1中的所有p两次。由于g(i+1,j-1)仅计算类别1中的所有P,因此减去该值即可得到g(i+1,j)+g(i,j-1)-g(i+1,j-1)给出类别1、2和3中的P总数。其余的P属于第4类。如果x[i]!=x[j]则该类别中没有PS;否则,在i+1或之后开始,在j-1或之前结束的PS的数量是相同的,即g(i+1,j-1),加上一个额外的2字符序列x[i]x[j][编辑:感谢commenter Tuxdude在此处提供了2个修复!]

    有了这个,我们可以用一种方式来表达g(),将二次型从f()变为常数时间:

    g(i, i) = 1 (i.e. when j = i)
    g(i, i+1) = 2 + (x[i] == x[i+1]) (i.e. 3 iff adjacent chars are identical, otherwise 2)
    g(i, j) = 0 when j < i (this new boundary case is needed)
    g(i, j) = g(i+1, j) + g(i, j-1) - g(i+1, j-1) + (x[i] == x[j])*(g(i+1, j-1)+1) when j >= i+2
    
    g(i,i)=1(即当j=i时)
    g(i,i+1)=2+(x[i]==x[i+1])(即3个iff相邻字符相同,否则为2)
    当j=i+2时,g(i,j)=g(i+1,j-1)+g(i+1,j-1)+(x[i]==x[j])*(g(i+1,j-1)+1)
    
    现在,最终答案是简单的g(1,n)。

    使用DP的直观O(n^3)解决方案:

    让每个状态dp(i,j)表示字符串[i…j]中回文子序列的数量 然后给出了简单的递推公式

    for k in range i, j-1:
        if(A[j]==A[k]){
            dp(i,j) = dp(i,j) + dp(k+1,j-1);
    
    这个想法很简单。。用于添加新字符,请检查它是否为子序列的结尾。如果在先前计算的较小子问题中存在相同的字符,那么它将添加范围(k+1,j-1)中包含的子序列数。 处理好街角的案子就行了。 添加一个,因为新添加的字符也是单个字符的子序列。
    即使范围(k+1,j-1)中没有子序列,仍然会得到1个长度为2的新子序列(如“aa”)。

    aba的答案是什么?你真的是指子序列,还是子序列?我只指子序列。“aba”的答案是‘a’、‘b’、‘a’、‘aa’、‘aba’,即5。正如您所看到的,“aa”实际上是一个子序列,而不是子字符串。这可以帮助您找到str1是否是str2的子序列,然后运行此命令检查string@therealprashant对不起,那有什么用?我们必须计算回文子序列的数量,以及你所建议的方法的空间和时间复杂度是多少?与其只查找最长的序列,不如将找到的所有回文和每个回文的子序列都保存在内存中。我并没有完全得到你的解。你能在“aab”上运行它吗?另外,你能看一下问题的第二部分吗?f(i,j)=??如果x[i]=x[j]在“aab”上,我们得到f(1,1)=f(2,2)=f(3,3)=1,f(1,2)=1+0(因为x[1]=x[2]),f(2,3)=0(因为x[2]!=x[3]),f(1,3)=0(因为x[1]!=x[3])。总数:1+1+1+1+0+0=4。(注意:我编辑了答案以修复一个bug。)问题的第二部分似乎要难得多,或者至少我认为