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

Algorithm 具有特定属性的最长公共子序列?,algorithm,dynamic-programming,Algorithm,Dynamic Programming,我们说,一个数序列x(1),x(2),…,x(k)是之字形,如果它的三个连续元素没有创建一个非递增或非递减序列。更准确地说,对于所有i=1,2,…,k-2 x(i) >( x(i+1),x(i-1) ) or x(i) < ( x(i+1) , x(i-1)) 我使用 magic(0,0,Integer.MIN_VALUE,0); 这里使用Integer.MIN_值作为一个哨兵值,表示序列中还没有数字。 功能如下所示: static int magic(int i, in

我们说,一个数序列x(1)x(2),…,x(k)之字形,如果它的三个连续元素没有创建一个非递增非递减序列。更准确地说,对于所有i=1,2,…,k-2

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

or

x(i) < ( x(i+1) , x(i-1))
我使用

magic(0,0,Integer.MIN_VALUE,0);
这里使用Integer.MIN_值作为一个哨兵值,表示序列中还没有数字。 功能如下所示:

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;

 }
static int magic(int i,int j,int last,int direction){
如果(hm.containsKey(i+“”+j+“”+last+“”+方向))
返回hm.get(i+“”+j+“”+last+“”+方向);
如果(i==seq1.length | j==seq2.length){
返回0;
}
int take_eath=0,leave_eath=0,leave1=0,leave2=0;
if(seq1[i]==seq2[j]&&last==Integer.MIN_值)
取1+magic(i+1,j+1,seq1[i],方向);//这是第一个数字,因此方向为0。
else if(seq1[i]==seq2[j]&(方向==0 | |方向==1&&seq1[i]>last | |方向==-1&&seq1[i]seq1[i]?1:-1):2);
左/右=魔法(i+1,j+1,最后,方向);
LEVE1=魔术(i+1,j,最后一个,方向);
leave2=魔法(i,j+1,最后一个,方向);
INTANS;
ans=Math.max(Math.max(Math.max)(两个都取,两个都留),leave1,leave2);
hm.put(i+“”+j+“”+last+“”+方向,ans);
返回ans;
}
现在,上面的代码正在为我所能制作的尽可能多的测试用例工作,但是复杂度很高。
如何降低时间复杂度,我可以在这里消除一些状态变量吗?有没有一种有效的方法可以做到这一点?

首先,让我们减少状态的数量:让
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))
向下
方向也有类似的情况。用一种简单的方法解决这个问题 将导致类似于O(n4·W)的运行时,其中W是数组中整数的范围。W不是多项式有界的,所以我们一定要去掉这个因子,理想情况下是一对n因子

为了解第一部分,你必须找到最大的f(i',j',up)和 我和j。这是一个标准的二维正交范围最大查询

对于第二种情况,您需要用i'>i,j'>j和s1[i']>s1[i]找到最大值(i',j',down)。这是矩形(i,∞) x(j,∞) x(s1[i],∞).

现在这里有3个维度看起来很吓人。但是,如果我们按照i的降序处理状态,那么我们可以去掉1个维度。 因此,我们将问题简化为矩形(j,∞) x(s1[i],∞). 坐标压缩将值的维数降低到O(n)

您可以使用二维数据结构(如二叉树或二叉索引树)在O(log2n)中解决这两种范围查询。总运行时间为O(n2·log2n)


您可以使用分数级联消除一个日志因子,但这与一个高常数因子有关。运行时仅比查找最长公共子序列的日志因子少一个日志因子,这似乎是我们问题的下界。

您能给出一个公共序列的示例吗?您是指公共序列的位置吗通过数值计算?1,2,1,2,1,2,1/3,2,3,2,3,2…正确的结果是什么?流露出我的无知,但你能更详细地解释重复发生吗?你是如何将其减少到三个变量的?@Sam重要的认识是,如果你强迫元素i和j实际上是序列的一部分,你就能从中知道最后一个值,然后您不需要在状态中显式地表示它
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))