具有一定属性的最长公共子序列?

时间:2016-02-11 08:56:53

标签: algorithm dynamic-programming

我们说一系列数字 x(1) x(2),..., x(k)是如果没有三个连续元素创建非增加非减少序列,则 zigzag 。更确切地说,对于所有i = 1,2,...,k-2

x(i) >( x(i+1),x(i-1) )  

or

x(i) < ( x(i+1) , x(i-1))

我有两个数字序列 a(1),a(2),...,a(n) b(1),b(2),.. 。,b(M)即可。问题是计算其最长的常见锯齿形子序列的长度。换句话说,您将从两个序列中删除元素,使它们相等,这样它们就是一个Z字形序列。如果执行此操作所需的最小元素数为k,那么您的答案为m + n-2k。

注意。长度为2和1的序列通常为Z字形

现在我尝试使用以下状态变量编写一个memoized递归解决方案

i= current position of sequence 1.
j= current position of sequence 2.
last= last taken number in the zigzag sequence currently being considered.
direction = current requirement of the number i.e. should it be greater than previous,less or same;  

我用

调用下面的函数
magic(0,0,Integer.MIN_VALUE,0);

这里使用Integer.MIN_VALUE作为一个标记值,表示序列中尚未采集任何数字。 功能如下:

static int magic(int i, int j, int last, int direction) {

  if (hm.containsKey(i + " " + j + " " + last + " " + direction))
   return hm.get(i + " " + j + " " + last + " " + direction);


  if (i == seq1.length || j == seq2.length) {
   return 0;

  }



  int take_both = 0, leave_both = 0, leave1 = 0, leave2 = 0;
  if (seq1[i] == seq2[j] && last == Integer.MIN_VALUE)
   take_both = 1 + magic(i + 1, j + 1, seq1[i], direction); // this is the first digit  hence direction is 0.
  else if (seq1[i] == seq2[j] && (direction == 0 || direction == 1 && seq1[i] > last || direction == -1 && seq1[i] < last))
   take_both = 1 + magic(i + 1, j + 1, seq1[i], last != seq1[i] ? (last > seq1[i] ? 1 : -1) : 2);


  leave_both = magic(i + 1, j + 1, last, direction);

  leave1 = magic(i + 1, j, last, direction);
  leave2 = magic(i, j + 1, last, direction);
  int ans;

  ans = Math.max(Math.max(Math.max(take_both, leave_both), leave1), leave2);
  hm.put(i + " " + j + " " + last + " " + direction, ans);
  return ans;

 }

现在上面的代码适用于我可以做的尽可能多的测试用例,但复杂性很高。 如何减少时间复杂度,我可以在这里消除一些状态变量吗?有没有一种有效的方法呢?

1 个答案:

答案 0 :(得分:0)

首先让我们减少状态数:让f(i, j, d)为从第一个字符串中的位置i开始的最长公共之字形序列的长度,并且在第二个字符串中的位置j并从方向d开始(向上或者下来)。

我们有复发

f(i, j, up) >= MAX(i' > i, j' > j : f(i', j', up))
if s1[i] = s2[j]:
    f(i, j, up) >= MAX(i' > i, j' > j, s1[i'] > x : f(i', j', down))

类似于down方向。以直截了当的方式解决这个问题 将导致类似O(n 4 ·W)的运行时,其中W是数组中整数的范围。 W不是多项式的,所以我们绝对想要摆脱这个因素,理想情况下还有两个因素。

要解决第一部分,你必须找到最大的f(i',j',up) 我&gt;我和j'&gt;学家这是标准的标准2-d正交范围最大查询。

对于第二种情况,您需要找到i'和gt;的最大值(i',j',down)我,j'&gt; j和s1 [i']&gt; S1 [i]中。这是矩形中的范围最大查询(i,∞)x(j,∞)x(s1 [i],∞)。

现在这里有3个尺寸看起来很吓人。但是,如果我们按照i的递减顺序处理状态,那么我们就可以摆脱一个维度。 因此,我们将问题简化为矩形(j,∞)x(s1 [i],∞)中的范围查询。坐标压缩将值的维度降低到O(n)。

您可以使用二维数据结构(例如range tree或二叉索引树)来解决O(log 2 n)中的两种范围查询。总运行时间为O(n 2 ·log 2 n)。

您可以使用分数级联来消除一个对数因子,但这与高常数因子相关联。然后运行时只有一个对数因子,用于找到最长的公共子序列,这似乎是我们问题的下限。