最大子序列、最长公共子串、最长公共子序列_无限长度的最大子序列_sumi的博客-程序员秘密

技术标签: iterator  c  string  动态规划  buffer  up  pair  

最大子序列
最大子序列是要找出由数组成的一维数组中和最大的连续子序列。比如{5,-3,4,2}的最大子序列就是 {5,-3,4,2},它的和是8,达到最大;而 {5,-6,4,2}的最大子序列是{4,2},它的和是6。你已经看出来了,找最大子序列的方法很简单,只要前i项的和还没有小于0那么子序列就一直向后扩展,否则丢弃之前的子序列开始新的子序列,同时我们要记下各个子序列的和,最后找到和最大的子序列。

代码如下:

#include<iostream>
usingnamespacestd;
intMaxSubSeq(constint*arr,intlen,int*start,int*end){
    intmax=0;                 //记录目前找到的最大子序列的和
    intsum=0;                 //记录当前子序列的和
    intbegin=0,finish=0;      //记录当前子序列的起始下标
    *start=begin;*end=finish;  //记录最长子序列的起始下标
    for(inti=0;i<len;i++){
        sum+=arr[i];
        finish=i;
        if(sum>max){
            max=sum;
            *end=finish;
            *start=begin;
        }
        if(sum<=0){
            sum=0;
            begin=i+1;
        }
    }
    returnmax;
}
intmain(){
    intarr[6]={5,-3,-2,12,9,-1};
    intstart,end;
    intmax=MaxSubSeq(arr,6,&start,&end);
    cout<<"The MaxSubSeq is from position "<<start<<"to position "<<end<<"."<<endl;
    cout<<"Sum of MaSubSeq: "<<max<<endl;
    return0;
}


最长公共子串(LCS)

找两个字符串的最长公共子串,这个子串要求在原字符串中是连续的。其实这又是一个序贯决策问题,可以用动态规划来求解。我们采用一个二维矩阵来记录中间的结果。这个二维矩阵怎么构造呢?直接举个例子吧:"bab"和"caba"(当然我们现在一眼就可以看出来最长公共子串是"ba"或"ab")

   b  a  b

c  0  0  0

a  0  1  0

b  1  0  1

a  0  1  0

我们看矩阵的斜对角线最长的那个就能找出最长公共子串。

不过在二维矩阵上找最长的由1组成的斜对角线也是件麻烦费时的事,下面改进:当要在矩阵是填1时让它等于其左上角元素加1。

   b  a  b

c  0  0  0

a  0  1  0

b  1  0  2

a  0  2  0

这样矩阵中的最大元素就是 最长公共子串的长度。

在构造这个二维矩阵的过程中由于得出矩阵的某一行后其上一行就没用了,所以实际上在程序中可以用一维数组来代替这个矩阵。

代码如下:

#include<iostream>
#include<cstring>
#include<vector>
usingnamespacestd;
//str1为横向,str2这纵向
conststring LCS(conststring& str1,conststring& str2){
    intxlen=str1.size();      //横向长度
    vector<int> tmp(xlen);        //保存矩阵的上一行
    vector<int> arr(tmp);     //当前行
    intylen=str2.size();      //纵向长度
    intmaxele=0;              //矩阵元素中的最大值
    intpos=0;                 //矩阵元素最大值出现在第几列
    for(inti=0;i<ylen;i++){
        string s=str2.substr(i,1);
        arr.assign(xlen,0);    //数组清0
        for(intj=0;j<xlen;j++){
            if(str1.compare(j,1,s)==0){
                if(j==0)
                    arr[j]=1;
                else
                    arr[j]=tmp[j-1]+1;
                if(arr[j]>maxele){
                    maxele=arr[j];
                    pos=j;
                }
            }      
        }
//      {
//          vector<int>::iterator iter=arr.begin();
//          while(iter!=arr.end())
//              cout<<*iter++;
//          cout<<endl;
//      }
        tmp.assign(arr.begin(),arr.end());
    }
    string res=str1.substr(pos-maxele+1,maxele);
    returnres;
}
intmain(){
    string str1("21232523311324");
    string str2("312123223445");
    string lcs=LCS(str1,str2);
    cout<<lcs<<endl;
    return0;
}


?

最长公共子序列-LCS(Longest Common Subsequence)

最长公共子序列与最长公共子串的区别在于最长公共子序列不要求在原字符串中是连续的,比如ADE和ABCDE的最长公共子序列是ADE。

我们用动态规划的方法来思考这个问题如是求解。首先要找到状态转移方程:

等号约定,C1是S1的最右侧字符,C2是S2的最右侧字符,S1‘是从S1中去除C1的部分,S2'是从S2中去除C2的部分。

LCS(S1,S2)等于下列3项的最大者:

(1)LCS(S1,S2’)

(2)LCS(S1’,S2)

(3)LCS(S1’,S2’)--如果C1不等于C2; LCS(S1',S2')+C1--如果C1等于C2;

边界终止条件:如果S1和S2都是空串,则结果也是空串。

下面我们同样要构建一个矩阵来存储动态规划过程中子问题的解。这个矩阵中的每个数字代表了该行和该列之前的LCS的长度。与上面刚刚分析出的状态转移议程相对应,矩阵中每个格子里的数字应该这么填,它等于以下3项的最大值:

(1)上面一个格子里的数字

(2)左边一个格子里的数字

(3)左上角那个格子里的数字(如果 C1不等于C2); 左上角那个格子里的数字+1( 如果C1等于C2)

举个例子:

       G  C  T  A

   0  0  0  0  0

G  0  1  1  1  1

B  0  1  1  1  1

T  0  1  1  2  2

A    0  1  1  2  3

填写最后一个数字时,它应该是下面三个的最大者:

(1)上边的数字2

(2)左边的数字2

(3)左上角的数字2+1=3,因为此时C1==C2

所以最终结果是3。

在填写过程中我们还是记录下当前单元格的数字来自于哪个单元格,以方便最后我们回溯找出最长公共子串。有时候左上、左、上三者中有多个同时达到最大,那么任取其中之一,但是在整个过程中你必须遵循固定的优先标准。在我的代码中优先级别是左上>左>上。

下图给出了回溯法找出LCS的过程:

奉上代码:

?
#include<iostream>
#include<cstring>
#include<stack>
#include<utility>
#define LEFTUP 0
#define LEFT 1
#define UP 2
usingnamespacestd;
intMax(inta,intb,intc,int*max){           //找最大者时a的优先级别最高,c的最低.最大值保存在*max中
    intres=0;         //res记录来自于哪个单元格
    *max=a;
    if(b>*max){
        *max=b;
        res=1;
    }
    if(c>*max){
        *max=c;
        res=2;
    }
    returnres;
}
//调用此函数时请注意把较长的字符串赋给str1,这主要是为了在回溯最长子序列时节省时间。如果没有把较长的字符串赋给str1不影响程序的正确执行。
string LCS(conststring &str1,conststring &str2){
    intxlen=str1.size();              //横向长度
    intylen=str2.size();              //纵向长度
    if(xlen==0||ylen==0)               //str1和str2中只要有一个为空,则返回空
        return"";
    pair<int,int> arr[ylen+1][xlen+1];    //构造pair二维数组,first记录数据,second记录来源
    for(inti=0;i<=xlen;i++)        //首行清0
        arr[0][i].first=0;
    for(intj=0;j<=ylen;j++)        //首列清0
        arr[j][0].first=0;
    for(inti=1;i<=ylen;i++){
        chars=str2.at(i-1);
        for(intj=1;j<=xlen;j++){
            intleftup=arr[i-1][j-1].first;
            intleft=arr[i][j-1].first;
            intup=arr[i-1][j].first;
            if(str1.at(j-1)==s)        //C1==C2
                leftup++;
            intmax;
            arr[i][j].second=Max(leftup,left,up,&arr[i][j].first);
//          cout<<arr[i][j].first<<arr[i][j].second<<" ";
        }
//      cout<<endl;
    }      /*矩阵构造完毕*/
    //回溯找出最长公共子序列
    stack<int> st;
    inti=ylen,j=xlen;
    while(!(i==0&&j==0)){
        if(arr[i][j].second==LEFTUP){
            if(arr[i][j].first==arr[i-1][j-1].first+1)
                st.push(i);
            --i;
            --j;
        }
        elseif(arr[i][j].second==LEFT){
            --j;
        }
        elseif(arr[i][j].second==UP){
            --i;
        }
    }
    string res="";
    while(!st.empty()){
        intindex=st.top()-1;
        res.append(str2.substr(index,1));
        st.pop();
    }
    returnres;
}
intmain(){
    string str1="GCCCTAGCG";
    string str2="GCGCAATG";
    string lcs=LCS(str1,str2);
    cout<<lcs<<endl;
    return0;
}
java版本:
publicstatic<E> List<E> longestCommonSubsequence(E[] s1,E[] s2){
        int[][] num=newint[s1.length+1][s2.length+1];
        for(inti=1;i<s1.length;i++){
            for(intj=1;j<s2.length;j++){
                if(s1[i-1].equals(s2[j-1])){
                    num[i][j]=1+num[i-1][j-1];
                }
                else{
                    num[i][j]=Math.max(num[i-1][j],num[i][j-1]);
                }
            }
        }
        System.out.println("lenght of LCS= "+num[s1.length][s2.length]);
        ints1position=s1.length,s2position=s2.length;
        List<E> result=newLinkedList<E>();
        while(s1position!=0&& s2position!=0){
            if(s1[s1position-1].equals(s2[s2position-1])){
                result.add(s1[s1position-1]);
                s1position--;
                s2position--;
            }
            elseif(num[s1position][s2position-1]>=num[s1position-1][s2position]){
                s2position--;
            }
            else{
                s1position--;
            }
        }
        Collections.reverse(result);
        returnresult;
    }

std::endl是一个特殊值,称为操纵符(manipulator),将它写入输出流时具有输出换行的效果,并刷新与设备相关联的缓冲区(buffer)。通过刷新缓冲区用户可立即看到写入到流中的输出。

我在调试以上代码时在45行(cout<<endl;)处设置断点,结果发现“43行(cout<<arr[i][j].first<<arr[i][j].second<<" ";) 没有执行”,这就是因为43行末尾没有加endl,所以用户没有立即看到输出流中的数据。

转载自:http://www.cnblogs.com/zhangchaoyang/articles/2012070.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wconvey/article/details/7448298

智能推荐

[APK签名] jarsigner APK V1签名_bjxiaxueliang的博客-程序员秘密

jarsigner 对APK签名APK打包签名 涉及到两个工具 jarsigner、 apksigner,其对应的签名方案如下:v1 方案:基于 JAR 签名,采用的签名工具为 jarsignerv2 方案:APK 签名方案 v2,在 Android 7.0 引入,采用的签名工具为 apksignerv3 方案:APK 签名方案v3,在 Android 9.0 引入,采用的签名工具为 apksigner当前几乎所有的应用市场都要求采用V2以上签名方案,采用jarsigner签名的V1方案几乎不

Python调试之pdb_菜鸟入行的博客-程序员秘密

pdb是基于命令行的调试工具,类似于gnu的gdb(调试c/c++)pbd命令:l --&amp;gt; list显示当前代码c --&amp;gt; contiue 完整运行后面代码n --&amp;gt; 向下执行一行代码b --&amp;gt; break 用于查看所有断点或新增断点cl/clear --&amp;gt; 取消断点s --&amp;gt; 调用函数,进入函数内部p --&amp;gt; print 打印变量值a --&amp;gt; 查看...

node.js http web简易 服务器_aikongmeng的博客-程序员秘密

var http = require('http');var fs = require('fs');var url = require('url'); // 创建服务器http.createServer( function (request, response) { // 解析请求,包括文件名 var pathname = url.parse(request.url).p...

HDU 1166 敌兵布阵_ab7986590的博客-程序员秘密

初学线段树,还没学之前,以为线段树很难,学了之后才发现.......或许还没到难的地方吧这题也就是线段树的简单应用,也就是要求你在短时间内计算某一段的人数,和动态维护数组。本题采用树的线性储存结构,也就是a[i]为父节点,a[2*i]为a[i]的左孩子,a[2*i+1]为a[i]的右孩子,这样就是浪费省内存!虽然采用链表结构能省内存,但操作比较麻烦!!!树结点:1...

洛谷 P2004 领地选择 (前缀和)_洛谷不同的选择_然然zl的博客-程序员秘密

题目描述作为在虚拟世界里统帅千军万马的领袖,小 Z 认为天时、地利、人和三者是缺一不可的,所以,谨慎地选择首都的位置对于小 Z 来说是非常重要的。首都被认为是一个占地 C\times CC×C 的正方形。小 Z 希望你寻找到一个合适的位置,使得首都所占领的位置的土地价值和最高。输入格式第一行三个整数 N,M,CN,M,C,表示地图的宽和长以及首都的边长。接下来 NN 行每行 MM 个整数,表示了地图上每个地块的价值。价值可能为负数。输出格式一行两个整数 X,YX,Y,表示首都左上角的坐标。输

HTML DIV CSS 笔记汇总_明伯的博客-程序员秘密

HTML DIV CSS 笔记汇总博客分类: CSS+DIV布局心得与体会csshtml CSS 层叠样式表单 Ctrl+j 弹出自动提示,帮助. 鼠标放到元素上变形状(鼠标样式,鼠标形状变了):cursor: pointer; 元素的显示模式(display:none) 给控件做文本内容(即label来修饰文本) 婚

随便推点

Adobe PDF 虚拟打印失败的解决方案_chandler_li的博客-程序员秘密

当碰上 Adobe PDF Printer 的时候, 大多数使用默认设置的朋友都打错出这样一个记事本文件的错误记录   log: 结局方案:取消 仅依靠系统字体; 不使用文档字体 的复选框

cookie和sessionStorage和localStorage的区别_ALSNEI的博客-程序员秘密

cookie和sessionStorage和localStorage的区别共同点:都是保存在浏览器端,且同源的。不同: 2.1. 存储数据的生命周期 cookie: 可设置失效时间,默认是关闭浏览器后失效 localStorage: 手动清除,否则永久保存 sessionStorage:仅在当前网页中存在,直到关闭该网页或者浏览器后失效。 2.

Atitit 单项功能开发 最佳实践规范 标准化流程attilax总结.docx_attilax的博客-程序员秘密

Atitit 单项功能开发 最佳实践规范 标准化流程attilax总结.docx  1.1. 适用场景主要基于数据库项目 11.2. 找到界面界面模板或范例 11.3. 使用dw快速可视化设计界面,使用代码模式微调 11.4. Vue关联表格或form表单 11.5. Ajax请求数据绑定到表格,注意接口web服务器最好开启ajaxCORS跨域,方便file

Unity报错Unsupported D3D format 0x58_⅔紫澜戥℡的博客-程序员秘密

Unity报错Unsupported D3D format 0x58描述错误每当新的视频开始播放,或者视频中的分辨率发生变化(使用HLS)时,使用DX11时都会引发错误“不支持的D3D格式0x58”。该错误仅在Windows上发生,但似乎不会对质量或播放产生负面影响。解决办法: 升级AVPro插件至1.9.17及以上更换dll即可...

iOS进阶 - Block底层原理_masonry 底层原理 block_MYStrict的博客-程序员秘密

iOS进阶 - Block底层原理一 block的本质1 block本质上也是一个oc对象,它内部也有一个isa指针2 block是封装了函数调用以及函数调用环境的oc对象3 block的底层结构Block源码转换查看block在实际编译时无法转换成我们能够理解的源代码,但可以通过clang(LLVM编译器)转换成可读的源代码,步骤如下:1打开终端,输入cd 把要转换的文件拖到终端,然后回车进入要转换文件的目录;2输入命令clang -rewrite-objc main.m -o main.

Linux使用宝塔面板搭建网站,并内网穿透实现公网访问_宝塔内网穿透_远程内网穿透的博客-程序员秘密

宝塔面板作为简单好用的服务器运维管理面板,它支持Linux/Windows系统,我们可用它来一键配置LAMP/LNMP环境、网站、数据库、FTP等,通过Web端轻松管理服务器。以下教程,我们将演示使用宝塔面板快速简单搭建本地web网站,并做内网穿透,实现不在同个局域网下的用户也可以访问到本地web站点,无需公网IP,也不用设置路由器。安装apache服务器,在宝塔面板中我们点击网站,然后会提示安装apache服务器。选择极速安装然后等待安装完成即可,安装完成在左边消息列表会提示打开宝塔终端命令窗口,使用cp

推荐文章

热门文章

相关标签