Algorithm 具有特定属性的最长公共子序列?
我们说,一个数序列x(1),x(2),…,x(k)是之字形,如果它的三个连续元素没有创建一个非递增或非递减序列。更准确地说,对于所有i=1,2,…,k-2Algorithm 具有特定属性的最长公共子序列?,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(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))