裸题:
给两串序列,求其中最长的公共子序列的长度。
如:
A:1232193
B:482914132
最长公共子序列:232/213/293…总之长度为3。数组
固然也有一种变式,题意以下。
两条平行线上各有n,m个点,已知每一个点的坐标,一个点只能与另外一条线上的点连一条线段,两点连成的线段不能相交。问最多连多少条线段。(线段不相交问题)markdown
如何计算?
咱们发现这个问题是一个明显的动态规划。
经过求子序列的最长公共子序列来获得更长的序列的最长公共子序列。spa
那么如何构造转移方程?
用dp[i][j]表示第一序列到第i个,第二序列到第j个的最长子序列长度。debug
那么转移方程式以下:调试
if(a[i] == b[j]) dp[i][j] = dp[i-1][j-1]+1;
else dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
若是当前位置的两个数(字符)相同,那么它的”基础“不能取到dp[i][j-1]或dp[i-1][j]。由于a[i]和b[j]要在本次用到。code
在没有滚动数组的状况下,空间复杂度和时间复杂度都是O(n*m),即O(N^2)。
那么完整代码以下:string
void Dp1()
{
for(unsigned i = 1; i != n+1; ++i)
{
for(unsigned j = 1; j != m+1; ++j)
{
if(a[i-1] == b[j-1])
{
dp[i][j] = dp[i-1][j-1]+1;
}
else
{
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
debug("%d ", dp[i][j]); //调试输出
}
debug("\n"); //同上
}
return ;
}
咱们能够发现转移方程只用到了dp[i-1][j],dp[i][j-1],dp[i-1][j-1],至于dp[i-2][j]等根本没有用到,因此咱们能够滚动数组,只留下两行(一行存上一次,一行存当前)。
空间复杂度降到O(2*m),即O(N)。
只须要把转移方程式改一下:class
void Dp()
{
for(unsigned i = 1; i != n+1; ++i)
{
for(unsigned j = 1; j != m+1; ++j)
{
if(a[i-1] == b[j-1])
{
dp[i&1][j] = dp[(i&1)^1][j-1]+1;
}
else
{
dp[i&1][j] = max(dp[(i&1)^1][j], dp[i&1][j-1]);
}
debug("%d ", dp[i][j]);
}
debug("\n");
}
return ;
}
利用位运算:效率
x&1 == x%2//异
(x&1)^1 == (i-1)%2
效果相同可是效率更高。基础
自此结束。
箜瑟_qi 2017.04.25 17:57