【问题标题】:Total number of palindromic subsequences in a string字符串中回文子序列的总数
【发布时间】:2015-05-05 09:52:43
【问题描述】:

问题是这样的--

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

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

我想到了寻找最长回文子序列的动态编程解决方案,因此试图从中汲取灵感。无法真正得到解决方案。可能甚至不需要动态编程。请提出建议。

还有一个问题。当去掉“不一定要不同”的条件后,我们还能在不实际生成所有回文子序列的情况下进行计数吗?

【问题讨论】:

  • aba 的答案是什么?你真的是指子序列还是子字符串?
  • 我只指子序列。 "aba" 的答案是 'a', 'b', 'a', 'aa','aba' 即 5。如您所见,'aa' 实际上是一个子序列而不是子字符串。
  • geeksforgeeks.org/… 这可以帮助您查找 str1 是否是 str2 的子序列,然后运行它来检查字符串的每个部分
  • @therealprashant 很抱歉,但这有什么帮助?我们必须计算回文子序列的数量,以及您建议的方法的空间和时间复杂度是多少?
  • 不要只查找最长的序列,而是将所有找到的回文和每个回文的子序列都保存在内存中

标签: algorithm data-structures dynamic-programming


【解决方案1】:

[编辑 19/10/2015:一位匿名审稿人指出了公式的一个问题,这促使我注意到另一个更大的错误……现在已修复。]

我现在知道如何将求解时间缩短到 O(n^2)。我会留下我的另一个答案,以防它作为这个答案的垫脚石有趣。注意:这(也)只是问题第一部分的解决方案;我认为没有办法仅有效地计算 distinct 回文子序列 (PS)。

与其计算恰好在 i 和 j 位置开始和结束的 PS 的数量,让我们计算有多少个 PS 开始于 或之后 i 并结束于 或之前 j.称之为 g(i, j)。

我们可以试着写成 g(i, j) = g(i, j-1) + g(i+1, j) + (x[i] == x[j])*g(i+ 1, j-1) 对于 j > i 的情况。但这并不完全奏效,因为前两个术语将重复计算任何从 i 开始并以 j 结束的 PS。

关键的见解是注意到,我们可以通过减去 g() 的其他值,或者添加更多的g() 重新打开以补偿重复计算。例如,从 exactly i 开始并在 exactly j 结束的 PS 的数量是 g(i, j) - g(i+1, j) - g( i, j-1) + g(i+1, j-1):最后一项纠正了第二项和第三项都计算所有以 开头的 g(i+1, j-1) PS 的事实之后 i 和结束之前 j.

从 i 或之后开始并在 j 或之前结束的每个 PS 恰好属于 4 个类别中的 1 个:

  1. 在 i 之后开始,在 j 之前结束。
  2. 从 i 开始,在 j 之前结束。
  3. 在 i 之后开始,在 j 结束。
  4. 从 i 开始,在 j 结束。

g(i+1, j) 统计1或3类的所有PS,而g(i, j-1) 统计1或2类的所有PS,所以它们的和g(i+1, j) + g(i, j-1) 对第 2 类或第 3 类中的所有 PS 分别计数一次,对第 1 类中的所有 PS 计数两次。由于 g(i+1, j-1) 仅计算类别 1 中的所有 PS,因此减去此得到 g(i+1, j) + g(i, j-1) - g(i+1, j- 1) 给出类别 1、2 和 3 中 PS 的总数。剩余的 PS 是类别 4 中的 PS。如果 x[i] != x[j] 则该类别中没有 PS;否则,与在 i+1 或之后并在 j-1 或之前结束的 PS 的数量完全相同,即 g(i+1, j-1),加上一个额外的 2 字符序列 x [i]x[j]。 [编辑:感谢评论者 Tuxdude 在这里修复了 2 个问题!]

有了这个,我们可以用将二次情况从 f() 更改为常数时间的方式来表达 g():

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(1, n)。

【讨论】:

  • 你是怎么得出g(i,j)的定义的?这不是很直观。
  • g(i, i + 1) 也不应该是 2 (iff x[i] != x[i+1])3 (iff x[i] == x[i+1]) 吗?
  • 我通过一个示例重新考虑了您的解决方案 - aaa。正确的结果应该是 7,但根据您的计算,它只会是 6。再次查看该方法时,在计算 exactly starting at and exactly ending at 部分的附加总和时出现错误。我觉得你的计算过于复杂了。你应该只使用g(i + 1, j -1) + 1 这部分。仅使用字符 x[i]x[j] 获得的 PS 需要 1
  • @Tuxdude:谢谢!你在这两个方面都是绝对正确的。在后一种情况下,我不是简单地使计算过于复杂,而是肯定计算错误。我现在已经编辑了。
  • 感谢您更新答案。我仍然看到你离开了我提到的+1 部分。您需要考虑仅使用连接在一起的字符 x[i] 和 x[j] 获得的一个额外 PS(这将是一个 PS,因为 x[i] == x[j])。递归关系的最后一行应声明: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 &gt;= i+2
【解决方案2】:

这是一个可怕的 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)对所有1

DP 为 O(n^4),因为有 n^2 个表条目,计算每个条目需要 O(n^2) 时间。 (通过利用 x[i] != x[j] 暗示 f(i, j) = 0 的事实,可以将其加速到至少 O(n^3)。)

【讨论】:

  • 我没有完全得到您的解决方案。你能在“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。(注意:我编辑了答案以修复错误。)
  • 问题的第二部分似乎要困难得多,或者至少我想不出一种有效解决它的方法。
  • 关于“f(i,j) = ?? if x[i] == x[j]”:思路是按顺序审理列出的3个案件,一审一审条件满足则视为匹配。所以如果 x[i] == x[j] 则 case 1 的条件失败,所以我们尝试 case 2。如果 i == j 则我们立即匹配 case 2 并返回 1;否则,我们直接进入案例 3。
【解决方案3】:

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

令每个状态dp(i,j)表示string[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”)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-04
    • 2021-07-07
    • 2011-04-10
    • 2021-12-08
    • 1970-01-01
    相关资源
    最近更新 更多