网站:CSUST 8月3日(LCS,LIS,LCIS)

LCS:      以下讲解来自:http://blog.csdn.net/yysdsyl/article/details/4226630

【问题】 求两字符序列的最长公共字符子序列

问题描述:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列。令给定的字符序列X=“x0,x1,…,xm-1”,序列Y=“y0,y1,…,yk-1”是X的子序列,存在X的一个严格递增下标序列<i0,i1,…,ik-1>,使得对所有的j=0,1,…,k-1,有xij=yj。例如,X=“ABCBDAB”,Y=“BCDB”是X的一个子序列。

考虑最长公共子序列问题如何分解成子问题,设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]。

问题的递归式写成:

回溯输出最长公共子序列过程:

接下来放一个输出最长公共子序列的长度及序列的代码:

 #include <iostream>
#include <string>
#include <string.h>
using namespace std;
int c[][];
int b[][];
int LCS_Length(string x,string y)
{
int m=x.length();
int n=y.length();
int i,j;
memset(c,,sizeof(c));//根据递归方程的第一种情况,先初始化数组c[][]
for(i=;i<=m;i++)
for(j=;j<=n;j++)
{//递归方程case 2
if(x[i-] == y[j-])
{
c[i][j]=c[i-][j-]+;
b[i][j]=; //表示
}
else if(c[i-][j] >= c[i][j-]) //下面是递归方程case3
{
c[i][j]=c[i-][j];
b[i][j]=; //表示↑
}
else
{
c[i][j]=c[i][j-];;
b[i][j]=; //表示←
}
}
return c[m][n];
}
void Print_LCS(string X,int i,int j)//输出最优解
{
if( (i == ) || (j == ) )
return;
if(b[i][j] == )
{
Print_LCS(X,i-,j-);
cout<<X[i-]<<" ";
}
else if(b[i][j] == )
Print_LCS(X,i-,j);
else
Print_LCS(X,i,j-);
} int main()
{
string X,Y;
while(cin>>X>>Y)
{
int p=LCS_Length(X,Y);
cout<<"这两个字符串的LCS为:"<<p<<endl;
cout<<"该公共子序列为:";
Print_LCS(X,X.length(),Y.length());
cout<<endl;
}
return ;
}

接下来是这一次比赛的题目:

A 大意是:求最长公共子序列。   Common Subsequence       HDU 1159    62MS

 #include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
using namespace std;
char a[],b[];
short dp[][];
int n,l1,l2;
int maxx(int i,int j)
{
return i>j?i:j;
}
int main()
{
while(~scanf("%s %s",a,b))
{
l1=strlen(a); //长度
l2=strlen(b);
int i,j;
memset(dp,,sizeof(dp));
for(i=;i<=l1;i++)
for(j=;j<=l2;j++)
{
if(a[i-]==b[j-])
dp[i][j]=dp[i-][j-]+;
else
dp[i][j]=maxx(dp[i-][j],dp[i][j-]);
}
printf("%d\n",dp[l1][l2]);
}
}

B 大意是:求回文字符串要不的个数。方法:把字符串a,反序得b,在把a,b的最长公共子序列求出来,用a的长度n-最长公共子序列的长度。

Palindrome      HDU 1513     484MS

先是代码,再是我的吐槽。

 #include<stdio.h>
#include<string.h>
char a[],b[];
short dp[][];
int maxx(int i,int j)
{
return i>j?i:j;
}
int main()
{
int n,j,i;
while(~scanf("%d",&n))
{
scanf("%s",a);
memset(dp,,sizeof(dp));
for(i=;i<n;i++) //反序得到b
b[i]=a[n-i-];
for(i=;i<=n;i++)
for(j=;j<=n;j++)
{
if(a[i-]==b[j-])
dp[i%][j]=dp[(i-)%][j-]+;
else
dp[i%][j]=maxx(dp[(i-)%][j],dp[i%][j-]);
}
printf("%d\n",n-dp[n%][n]);
}
return ;
}

本来我的dp是开的dp[5001][5001],本来在POJ是没有超内存的,因为在POJ的内存限制的60000+,但是HDU和我们的比赛是32000+.......少了一半多,开dp[5001][5001]的内存是49000+,所以超内存了。但是因为在扫描的时候,每次只扫描两行,所以dp[i][j]就可以变为dp[i%2][j],所以dp[5001][5001]就可以改为dp[2][5002],这样内存就大大减小了。     本来我认为i,j可以分别%2,因为实际扫的时候只有关4个点:1自己所在的点,2右边的点,3下面的点,4斜下方的点。但是现在还没有找到可行的方法。

C   魔法串   HDU 4545

先是非最长公共子序列的代码:      0ms

 #include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#define N 1010
char a[N],b[N];
vector<char>c[];
int main()
{
int i,j,k,m,T,t=;
scanf("%d",&T);
char x,y;
while(T--)
{
scanf("%s%s",a,b);
scanf("%d",&m);
memset(c,,sizeof(c));
while(m--)
{
cin>>x>>y;
c[(int)(x-'a')].push_back(y); //可以变的记录下来
}
for(i=,k=;a[k]&&b[i];i++)
{
if(b[i]==a[k]) //如果相等,两列都向前+1
{
k++;
continue;
}
for(j=c[b[i]-'a'].size()-;j>=;j--)
if(c[b[i]-'a'][j]==a[k]) //不同的时候如果可以变得和a,一样则+1
{
k++;
break;
}
}
printf("Case #%d: ",t++);
if(k>=strlen(a))
printf("happy\n");
else printf("unhappy\n");
}
return ;
}

再是求最长公共子序列的代码:       218MS

 #include<iostream>
#include<string.h>
#include<string>
using namespace std;
int dp[][];
bool has[][]; //这个要开大一点
int maxi(int x,int y)
{
if(x>y)
return x;
else return y;
}
int main()
{
int i,j,t,m,count=,len1,len2;
char a,b;
string str1,str2;
cin>>t;
while(t--)
{
count=count+;
cin>>str1>>str2;
len1=str1.size();
len2=str2.size();
memset(dp,,sizeof(dp));
memset(has,0,sizeof(has));
cin>>m;
for(j=;j<=m;j++)
{
cin>>a>>b;
has[a][b]=;
}
for(i=;i<=len1;i++)
for(j=;j<=len2;j++)
{
if(str1[i-]==str2[j-]||has[str2[j-]][str1[i-]]==) //或者可以变一样的~~~~~
dp[i][j]=dp[i-][j-]+;
else dp[i][j]=maxi(dp[i-][j],dp[i][j-]);
}
cout<<"Case #"<<count<<": ";
if(dp[len1][len2]==len1)
cout<<"happy"<<endl;
else cout<<"unhappy"<<endl;
}
return ;
}

D 大意是:求最少有几个下降序列    最少拦截系统    HDU 1257      有个说法是求最长上升子序列的长度。

先是DP:    15MS     来自:http://blog.csdn.net/a_eagle/article/details/7237067

 #include <stdio.h>//dp[i]表示第i个导弹飞过来时需要的最少拦截装置
#include <string.h>
int main()
{
int n,i,j,max,h[],dp[];
while(~scanf("%d",&n))
{
memset(dp,,sizeof(dp));//初始化拦截装置都为0
max=-;
for(i=;i<=n;i++)
scanf("%d",&h[i]);//飞来的高度
for(i=;i<=n;i++)
for(j=i-;j>=;j--)
if(h[i]>h[j]&&dp[i]<dp[j]+)//如果在拦截中出现了非单调递减的
dp[i]=dp[j]+;
for(i=;i<=n;i++)
if(dp[i]>max)
max=dp[i]; //取最大值
printf("%d\n",max);
}
return ;
}

再是贪心:     46MS

 #include <iostream>
#include <string>
#include <algorithm>
#define MAX 100000000
using namespace std;
int height[];
int top;
void arrange(int n)
{
int i;
sort(height,height+top+);
for(i=;i<=top;i++)
if(height[i]>=n)
{
height[i]=n;
break;
}
if(i==top+)//引入新的导弹系统
{
top++;
height[top]=n;
}
}
int main()
{
int t;
while(cin>>t)
{
top=;
height[]=MAX;//初始可以阻挡任何高度
int height;
for(int i=;i<t;i++)
{
cin>>height;
arrange(height);
}
cout<<top+<<endl;
}
}

同E一样的方法......三观尽毁.....     15MS

 #include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
class Do
{
public:
int x,id;
}D[];
int main()
{
int n;
while(~scanf("%d",&n))
{
int i,minn;
memset(D,,sizeof(D));
for(i=;i<n;i++)
scanf("%d",&D[i].x);
int number=,j;
for(i=;i<n;i++)
if(D[i].id==) //找到最大的没有标记的
{
minn=D[i].x;
number++;
for(j=i+;j<n;j++)
if(D[j].x<minn && D[j].id==) //小于最小高度&&没有被标记
{
D[j].id=;
minn=D[j].x; //更新最小高度
}
}
printf("%d\n",number);
}
return ;
}

E 大意是:有N个娃娃,有自己高度和大小,小的可以放到大娃娃里,求最后剩下几个娃娃。  Nested Dolls   HDU 1677

代码:     78MS

 #include <iostream>
#include <algorithm>
#include <stdio.h>
using namespace std;
struct node
{
int w,h;
};
node a[];
bool cmp(node a,node b)
{
if(a.w!=b.w) //按W排序,接下来的就不用管W了,只要比h的大小
return a.w>b.w;
return a.h<b.h;
}
int visit[];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int m;
cin>>m;
for(int i=;i<m;i++)
scanf("%d %d",&a[i].w,&a[i].h);
sort(a,a+m,cmp); //排序
int cnt=;
visit[cnt++]=a[].h;
for(int i=;i<m;i++)
{
if(a[i].h>=visit[cnt-]) //高度大于等于前面w最小的娃娃的h,则又开一个新的娃娃
visit[cnt++]=a[i].h;
else
{
int l=,r=cnt;
while(l<r) //二分找到可以放进去的最小的娃娃
{
int mid=(l+r)/;
if(visit[mid]>a[i].h)r=mid;
else l=mid+;
}
visit[l]=a[i].h;
}
}
cout<<cnt<<endl;
}
return ;
}

下面介绍另一种方法:    468MS

 #include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
class Dolls
{
public:
int x,y,id;
}D[];
bool comp(Dolls a,Dolls b)
{
if(a.x==b.x) return a.y<b.y; //同上
return a.x>b.x;
}
int main()
{
int t,n;
scanf("%d",&t);
while(t--)
{
int i,minn;
scanf("%d",&n);
memset(D,,sizeof(D));
for(i=;i<n;i++)
scanf("%d%d",&D[i].x,&D[i].y);
sort(D,D+n,comp); //排序
int number=,j;
for(i=;i<n;i++)
if(D[i].id==) //找到W最大的没有标记的娃娃
{
minn=D[i].y;
number++;
for(j=i+;j<n;j++)
if(D[j].y<minn && D[j].id==) //高度《这个娃娃,并且没有被标记
{
D[j].id=; //标记
minn=D[j].y; //更新高度
}
}
printf("%d\n",number);
}
return ;
}

插花一下,先讲下最长上升子序列。

 #include <stdio.h>
#define MAX 100000
#define INF 100000000
int a[MAX],c[MAX],len;
int find(int L,int R,int x)
{
if(L==R) return L;
int mid=(L+R)>>;
if(c[mid]<x) return find(mid+,len,x); //二分
else return find(L,mid,x); //二分
}
int main()
{
int i,n,j;
while(scanf("%d",&n)!=EOF)
{
for(i=;i<n;i++)
scanf("%d",&a[i]);
len=; c[]=-INF;
for(i=;i<n;i++)
{
if(a[i]>c[len]) j=++len;
else j=find(,len,a[i]);
c[j]=a[i];
}
printf("%d\n",len);
}
}

另一种方法:

 #include <stdio.h>
#include <string.h>
int main()
{
int n,a[],map[],i,j,maxx,number; while(~scanf("%d",&n))
{
memset(map,,sizeof(map));
memset(c,,sizeof(c));
number =;
for(i=;i<n;i++)
scanf("%d",&a[i]);
for(i=;i<n;i++)
if(map[i]==) //找到第一个没被标记的
{
map[i]=;
number++; //总数+1
maxx=a[i];
for(j=i+;j<n;j++)
if(map[j]==&&a[j]<=maxx) //大于前面的数并且未被标记
{
map[j]=;
maxx=a[j]; //更新最大数
}
} printf("%d\n",number);
}
return ;
}

E 大意是求最长公共上升子序列。          15ms

 #include<cstdio>
#include<cstring>
int f[],a[],b[],i,j,t,n1,n2,max;
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n1);
for(i=;i<=n1;i++)
scanf("%d",&a[i]);
scanf("%d",&n2);
for(i=;i<=n2;i++)
scanf("%d",&b[i]);
memset(f,,sizeof(f));
for(i=;i<=n1;i++)
{
max=;
for(j=;j<=n2;j++)
{
if (a[i]>b[j]&&max<f[j])
max=f[j];
if (a[i]==b[j])
f[j]=max+;
}
}
max=;
for(i=;i<=n2;i++)
if (max<f[i])
max=f[i];
printf("%d\n",max);
}
}

F 大意是:求最长回文上升子序列的长度。       0MS

 #include <cstdio>
#include <cstring>
using namespace std;
int n,ans,a[],dp[];
inline void Max(int &a,const int b){if(b>a) a=b;}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
memset(dp,,sizeof dp);
for(int i=;i<n;i++)
scanf("%d",&a[i]);
ans=;
for(int k=n-;k>=;k--)
{
int mx=;
for(int i=;i<=k;i++)
{
if(a[i]<a[k])
Max(mx,dp[i]);
else if(a[i]==a[k])
dp[i]=mx+;
if(i<k)
Max(ans,dp[i]*);
else
Max(ans,dp[i]*-);
}
}
printf("%d\n",ans);
}
return ;
}

LCS,LIS,LCIS的更多相关文章

  1. LCS/LIS/LCIS 模板总结

    /************************* LCS/LIS/LCIs模板总结: *************************/ /*************************** ...

  2. LCS,LIS,LCIS学习

    for(int i = 1;i <= n;i++) { int dpmax = 0; for(int j = 1;j <= m;j++) { dp[i][j] = dp[i-1][j]; ...

  3. hdu 1423(LCS+LIS)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1423 好坑啊..还有公共串为0时的特殊判断,还有格式错误..看Discuss看知道除了最后一组测试数据 ...

  4. 【ACM程序设计】动态规划 第二篇 LCS&LIS问题

    动态规划 P1439 [模板]最长公共子序列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目描述 给出 1,2,-,n 的两个排列 P1 和 P2 ,求它们的最长公共子序列. ...

  5. LCS(记录路径)+LIS+LCIS

    https://blog.csdn.net/someone_and_anyone/article/details/81044153 当串1 和 串2 的位置i和位置j匹配成功时, dp[i][j]=d ...

  6. LCS+LIS

    #include<iostream> #include<string> using namespace std; string a,b; ][]; int main() { w ...

  7. Uva 10635 - Prince and Princess LCS/LIS

    两个长度分别为p+1和q+1的由1到n2之前的整数组成的序列,每个序列的元素各不相等,两个序列第一个元素均为1.求两个序列的最长公共子序列 https://uva.onlinejudge.org/in ...

  8. 【LCS,LIS】最长公共子序列、单调递增最长子序列

    单调递增最长子序列 时间限制:3000 ms  |  内存限制:65535 KB 难度:4   描述 求一个字符串的最长递增子序列的长度如:dabdbf最长递增子序列就是abdf,长度为4   输入 ...

  9. 最长上升序列 LCS LIS

    子序列问题 (一)一个序列中的最长上升子序列(LISLIS) n2做法 直接dp即可: ;i<=n;i++) { dp[i]=;//初始化 ;j<i;j++)//枚举i之前的每一个j ) ...

随机推荐

  1. 升级到VS2013常见问题

    问题1: Building an MFC project for a non-Unicode character set is deprecated 解决方法: 用于多字节字符编码 (MBCS) 的 ...

  2. HDU - 3622 Bomb Game(二分+2-SAT)

    题目大意:玩一个放炸弹游戏,有N次放炸弹的机会,每次放炸弹时,你都有两个位置能够选择.问怎样放炸弹,能使爆炸的炸弹的半径的最小值最大(炸弹爆炸半径能够控制,可是爆炸形成的圈不能有重叠部分) 解题思路: ...

  3. 最简单的C# Windows服务程序

    通过这个示例了解如何通过C#如何创建一个Windows服务程序. 工具/原料   Vistual Studio 2015 c# 方法/步骤     打开vs2015 文件->新建项目->V ...

  4. hdu 2602(经典01背包)

    Bone Collector Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  5. poj 1061(扩展欧几里得定理求不定方程)

    两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面.它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止.可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特 ...

  6. Codeforces--630K--Indivisibility(容斥)

     K - Indivisibility Crawling in process... Crawling failed Time Limit:500MS     Memory Limit:65536 ...

  7. 第2章 安装Nodejs Nodejs基础 课程介绍

    因为你做任何Nodejs应用,底层无非都是通过调用这些既有的开放的接口,来完成相应的功能.这个要注意,不同版本的Nodejs,接口不一定相同.甚至是相同的接口,使用规范也有区别.我们以这个版本来过这些 ...

  8. [JavaEE] JBoss主要版本下载链接一览

    URL: http://teddysun.com/260.html JBoss在2006年被 RedHat 收购.在各种 J2EE 应用服务器中,JBoss 是最受欢迎而且功能最为强大的应用服务器.不 ...

  9. GStreamer系列 - 基本介绍

    什么是Gstreamer? Gstreamer是一个支持Windows,Linux,Android, iOS的跨平台的多媒体框架,应用程序可以通过管道(Pipeline)的方式,将多媒体处理的各个步骤 ...

  10. Vue发布过程中遇到坑,以及webpack打包优化

    前言 这段时间,本人自己做了一个vue画面部署到自己的服务器上,发现运行速度慢的的惊人,虽然服务器很渣(本人没什么钱,只能租最差的服务器,主要是给自己学习用的),但是这样开发出来的网站简直不能用,所以 ...