您的位置:首页 > 其它

最长子序列问题:简单的动规

2016-04-22 22:57 253 查看
**

最长公共子序列

**

一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列。

看到这个问题,首先想思路。当然,穷举时最容易想到的办法,可以对序列A的所有子序列枚举,检查它是否也是序列B的子序列,从而判断他是否为A和B的公共子序列,最终记录出长度最长的公共子序列,这样问题就解决了。但是序列A有2^n个不同的子序列,所以穷举算法的时间复杂度为指数阶。

但是,最长公共子序列具有最优子结构性质。

设序列 A={a1,a2,……,ai-1,ai}, Ai-1={a1,a2,……,ai-1}

B={b1,b2,……,bj-1,bj}, Bj-1={b1,b2,……,bj-1},

C={c1,c2,……,ck-1,ck}, Ck-1={c1,c2,……,ck-1},

其中,C为A,B的最长公共子序列,则有:

①:如果ai == bj,则有ai=bj=ck,且Ai-1和Bj-1的最长公共子序列是Ck-1;

②:如果ai != bj,且ck !=ai,则有Ai-1和B的最长公共子序列是C;

③:如果ai != bj,且ck !=bj,则有A和Bj-1的最长公共子序列是C;

由以上性质可以得出,要找出两个序列的最长公共子序列,只需要对以下情况进行迭代就好了:

①:当ai==bj时,找出Ai-1和 Bj-1的最长公共子序列,然后在结尾加上ai或bj就好了。

②:当ai!=bj时,A和 Bj-1的最长公共子序列,还有Ai-1和 Bj的最长公共子序列的最大值。

③:迭代的终点就是:当i==0或j==0时,最长公共子序列自然是空。

那么,我之前写过两次动规的文章了,认真看完并理解完并自己敲出来的同学肯定已经对动规的思想很熟悉了。那么上面我给出了迭代的方法,你们应该可以直接秒杀这道题了。当然,我还是会给出我的代码,供大家参考。

#include <stdio.h>
#include <stdlib.h>

#define dp(M,N,length)  dp[(M)*(length)+(N)]

static int Length(int *, int *, int *, int, int);

int main()
{
int a[] = {1,3,5,9,8,2,6,7,4};
int b[] = {1,2,3,4,5,6,7,8,9};
int * x = a, * y = b;
int m = sizeof(a) / sizeof(a[0]);
int n = sizeof(b) / sizeof(b[0]);
int size = (m) * (n);
int * dp = (int *)malloc(size * sizeof(int));
for (int i = 0; i < m; i++)
dp(i,0,n) = 0;
for (int i = 0; i < n; i++)
dp(0,i,n) = 0;
printf("%d\n",Length(x,y,dp,m,n));
}

int Length(x, y, dp, m, n)
int * x, * y;
int * dp;
int m, n;
{
for (int i = 1; i < m; i++)
{
for (int j = 1; j < n; j++)
{
if (x[i-1] == y[j-1])
{dp(i,j,n) = dp(i-1,j-1,n) + 1;}
else if (dp(i-1,j,n) >= dp(i,j-1,n))
{dp(i,j,n) = dp(i-1,j,n);}
else
{dp(i,j,n) = dp(i,j-1,n);}
}
}
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
printf("%6d",dp(i,j,n));
printf("\n");
}
return dp(m-1,n-1,n);
}


然而,这段代码只求出了最长公共子序列的长度,没有求出最长公共子序列。那么这个问题留给大家自己去思考,我给个提示:只需要创建一个追踪数组用来追踪公共子序列长度变化时的位置就好了。很明显能看出来,算法的时间复杂度为O(m*n)。

那么下面还有一道题:

最长递增子序列

有的细心的同学已经发现了,我上面的代码的结果就是第一个序列的最长递增子序列,而我给的第二个序列是第一个序列的排序。哈哈,明白了?没错,只需要将原序列排序,然后求原序列与排序的序列的最长公共子序列,得到的结果就是最长递增子序列了。

当然,这个算法分为两步,第一步排序,使用好的排序算法,时间复杂度为O(n*logn),第二步求最长递增子序列,时间复杂度为O(n*n)。所以总的时间复杂度也是O(n*n)。

算法是一个大坑,但是你绝对不会后悔你入了算法坑。因为你会发现,入坑越深,越能挖到一些很棒的东西。

我是算法吹,以后会给大家带来更多精彩的算法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: