动态规划最长公共子序列求解以及内存优化
来源:互联网 发布:mac终端默认路径 编辑:程序博客网 时间:2024/06/10 09:08
本文乃本人原创,转载请声明出处;
一、什么是最长公共子序列
什么是最长公共子序列呢?举个简单的例子吧,一个数列S,若分别是两个或多个已知序列的子序列,且是所有符合条件序列中最长的,则S称为已知序列的最长公共子序列。
举例如下,如:有两个随机数列,1 2 3 4 5 6 和 3 4 5 8 9,则它们的最长公共子序列长度为3 3 4 5。
如果序列为 BDCABA 和 ABCBDAB 那么最长公共子序列长度为4 BCAB 或者 BCBA
一直不明白:最长公共子串和最长公共子序列的区别。
上网查了下,最长公共子串(Longest Common Substirng)和最长公共子序列(Longest Common Subsequence,LCS)的区别为:子串是串的一个连续的部分,子序列则是从不改变序列的顺序,而从序列中去掉任意的元素而获得新的序列;也就是说,子串中字符的位置必须是连续的,子序列则可以不必连续。
二、考虑了动态规划的性质就是讲一个问题分解为若干子问题,每个子问题最优时当前问题也达到最优 , 那么我们求BDCABA 和 ABCBDAB 的最长公共子序列就可分解为求 BDCA 和 ABCBDA的解 然后加上 BA和B 的解,这样说不清楚,那么先说一下最长公共子序列的3个规律
考虑最长公共子序列问题如何分解成子问题,设A=“a0,a1,…,am-1”,B=“b0,b1,…,bm-1”,并Z=“z0,z1,…,zk-1”为它们的最长公共子序列。不难证明有以下性质:
(1) 如果am-1=bn-1,则zk-1=am-1=bn-1,且“z0,z1,…,zk-2”是“a0,a1,…,am-2”和“b0,b1,…,bn-2”的一个最长公共子序列;
(2) 如果am-1!=bn-1,则若zk-1!=am-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列;
(3) 如果am-1!=bn-1,则若zk-1!=bn-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列。
这样,在找A和B的公共子序列时,如有am-1=bn-1,则进一步解决一个子问题,找“a0,a1,…,am-2”和“b0,b1,…,bm-2”的一个最长公共子序列;如果am-1!=bn-1,则要解决两个子问题,找出“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列和找出“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列,再取两者中较长者作为A和B的最长公共子序列。
求解:
引进一个二维数组c[][],用c[i][j]记录X[i]与Y[j] 的LCS 的长度,b[i][j]记录c[i][j]是通过哪一个子问题的值求得的,以决定搜索的方向。
我们是自底向上进行递推计算,那么在计算c[i,j]之前,c[i-1][j-1],c[i-1][j]与c[i][j-1]均已计算出来。此时我们根据X[i] = Y[j]还是X[i] != Y[j],就可以计算出c[i][j]。
问题的规律式写成:
求每一段最长公共子序列的过程:
上面这一段是借鉴于博客 http://blog.csdn.net/yysdsyl/article/details/4226630
黑色的意思是在这个地方 s1[i]=s2[j] 所以a[i][j]的值等于a[i-1][j-1]+1; 箭头就是指这个值是根据哪一个值的出来的
白色呢就是这两个位置的值不一样,比如 s1[4]=A, s2[3]=C(上面的s1s2都是从1开始,实际上是从0开始,所以上面的i,j减1才是在数组s1,s2里面对应的值,这里方便对应就用的i,j的值请自己写程序的时候注意对应i-1,j-1) , 两个值不同,所以就要比较其子问题的最长子序列,选择其中最长的作为当前的最长子序列的长度 即BDCA和AB的最长子序列长度1,和 BDC和ABC的长度2 相比较,2>1所以得出a[4][3]=2;
然后对此我们可以写出程序,利用一个二维数组,通过i,j的嵌套循环 对每一个 a[i][j]的位置利用上述的公式计算出其值,然后输出最后一个值 就是最长公共子序列了,
下面贴上代码
- #include<stdio.h>
- #include<string.h>
- #define max(a,b) ((a)>(b)?(a):(b))
- int a[1001][1001]={0}; //定义数组 针对字符串长度小于1000的
- int main()
- {
- int len1,len2,i,j;
- char s1[1001],s2[1001];
- while(~scanf("%s %s",s1,s2))
- {
- len1=strlen(s1);
- len2=strlen(s2);
- for(i=1;i<=len1;i++) //循环列
- {
- for(j=1;j<=len2;j++) //循环行
- {
- if(s1[i-1]==s2[j-1]) //如果这个s1[i-1]=s2[j-1]的字符相等
- a[i][j]=a[i-1][j-1]+1; //这个位置的值就为其左上角的值加1
- else
- a[i][j]=max(a[i-1][j],a[i][j-1]);//不相等的话就比较上面和左边的值,得到最大值
- printf("%d ",a[i][j]); //动态规划思想,保证子问题最优,即整体最优
- }
- printf("\n");
- }
- printf("最长公共子序列为: %3d\n",a[len1][len2]);
- }
- }
—————————————————————————————————————————————————————————
————————————————————————————————————————————————————————
内存优化的思考
这就是动态规划的算法,保持子问题最优,最后则整体最优了,
但是,上述最大问题就是内存,因为每一位的数字都不大,小于字符长度,所以可以用short int 代替int 这样内存优化一半,但是这样依然不是办法,如果字符长度为100000;那么你就没办法解决了,
然后我们可以观察,上面的公式
根据理解,你也可以发现,你对a[i][j]的取值只与3个值有关 字符相等时 a[i-1][j-1],不相等时 a[i-1][j]和a[i][j-1]中大的那个,那么,我们实际上只需要两行数组就ok了,一个是当前a[i]行和a[i-1]行,就可以确定当前值了,并且因为下面的行也只与上一行的值有关,最上面的几行就完全没用了,这样,那我们就可以利用滚动数组的效果,a[2][1001],
比如a[0][1001]的值为第2行的值0 1 1 1 1 2 2 那么我们a[1][1001]就当成当前行,
就可以利用a[0]行计算出a[1],0 1 1 2 2 2 2 ,然后 a[1]代表第3行,那么我们要进行第四行怎么运算呢,
第四行只与第三行有关,也就是当前的a[1],所以把a[0]当成第四行,然后通过a[1]的数据,计算出a[0](第四行) 0 1 1 2 2 3 3 ;就这样交替进行就完全可以把a[1001][1001]变成a[2][1001],那么要怎么实现数组的滚动呢
第一行是a[1],第二行是a[0]。第三行是a[1],第四行是a[0],那么只要一个a[i%2]就可以实现滚动了,那么a[i-1]就是a[(i+1)%2],这样实现了替换,代码就很简单了,只要把所有的a[i]换成a[i%2]就好了,
代码如下
- #include<stdio.h>
- #include<string.h>
- #define max(a,b) ((a)>(b)?(a):(b))
- int main()
- {
- register int i,j; //常用变量可以这样,提高运行速度
- int len1,len2,a[2][1001]; //数组减小了,这样可以把字符串大小扩大到几十万
- char s1[1001],s2[1001]; //几十万的字符的话就必须用int 了 因为short int 范围为 65535
- while(~scanf("%s %s",s1,s2))
- {
- len1=strlen(s1);
- len2=strlen(s2);
- memset(a,0,sizeof(a)); //就两行,因为第一行必须初始化为0,所以加一个置0函数
- for(i=1;i<=len1;i++)
- {
- for(j=1;j<=len2;j++)
- {
- if(s1[i-1]==s2[j-1]) //比较字符相等
- a[i%2][j]=a[(i+1)%2][j-1]+1;//进行当前行的计算,下一次的时候就自动滚动了
- else
- a[i%2][j]=max(a[i%2][j-1],a[(i+1)%2][j]); //依然是自动计算
- printf("%d ",a[i%2][j]);
- }
- printf("\n");
- }
- printf("最长公共子序列长度为:%d\n",a[len1%2][len2]);
- }
- }
这样内存就被优化了,这个是我自己研究的,如果有更好地办法请回复通知我,因为很多人的速度都可以比我快,内存不知道能不能只用4个数表达,但是运行速度可以继续提升,还有优化办法;
- 动态规划最长公共子序列求解以及内存优化
- 动态规划求解最长公共子序列
- 动态规划求解最长公共子序列
- 动态规划求解最长公共子序列
- 动态规划求解最长公共子序列问题
- 最长公共子序列(动态规划求解)
- 动态规划求解最长公共子序列问题
- 最长公共子序列的动态规划求解
- 最长公共子序列的动态规划求解
- 最长公共子序列求解:递归与动态规划方法
- 动态规划求解最长公共子序列(LCS)
- ACM--最长公共子序列 动态规划求解
- C++实现动态规划求解最长公共子序列
- 最长公共子序列求解:递归与动态规划方法
- 最长公共子序列求解:递归与动态规划方法
- 最长公共子序列问题(动态规划求解)
- 使用动态规划求解最长公共子序列(LCS)
- 动态规划求解最长公共子序列(LCS)
- Struts文件上传
- 在firefox的搜索栏中 添加百度搜索栏
- linux内核字符串函数的实现
- Problem 2150 Fire Game
- [Spring]梦创俱乐部网站后台开发笔记
- 动态规划最长公共子序列求解以及内存优化
- android中的Fragment
- 关于对新装Linux的固态硬盘(SSD)做优化配置
- 新辰:一位老SEOer给新手的忠告 人脉才是重中之重!
- begin
- android基于xmpp的即时通讯应用
- cocos2dx下的A星算法
- JSON入门教程
- Solid State Drives