最长公共子序列 & 最长不减(不增)子序列-例题题解【poj1458,poj2250,poj1159】

7 篇文章 0 订阅
5 篇文章 0 订阅

最长公共子序列:

对A序列和B序列


(1) i=0,j=0 dp[i][j]=0;

(2) X[I]==Y[j]dp[i][j]=dp[i-1][j-1]+1;

X={A B C B }   Y={B D C AB}   Z={B C }->Z={B C B }


(3) X[I] != Y[j] dp[i][j]=max{dp[i][j-1],dp[i-1][j-1]}

X={A B C B  D}   Y={B D C AB}   

Z1={B C B}  (去掉Xi(这个例子是D)之后,也就是Xi-1和Yj 的最长公共字序列)

Z2={B C}  (去掉Yj(这个例子是B)之后,也就是Xi和Yj-1 的最长公共字序列)


由于每次的动态规划都是只和上一次有关,所以也可以用滚动数组记录。

比如下面的poj1159



POJ1458

#include <cstring>
#include <cstdio>
#include <algorithm>
#include <iostream>
#define maxn 1005
using namespace std;

char s[maxn],ss[maxn];
int dp[maxn][maxn];
int main(){
	while(scanf("%s%s",s,ss)!=EOF){
		int len1=strlen(s);
		int len2=strlen(ss);
		
		memset(dp,0,sizeof(dp));
		
		for(int i=1;i<=len1;i++){
			for(int j=1;j<=len2;j++){
				if(s[i-1]==ss[j-1]){
					dp[i][j]=dp[i-1][j-1]+1;
				}
				else{
					dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
				}
			}
		}		
		//注意i,j的初始值和意义 
		//如果是i=0,j=0开始
		/*
			for(int i=0;i<len1;i++){
			for(int j=0;j<len2;j++){
				if(s[i]==ss[j]){
					dp[i+1][j+1]=dp[i][j]+1;
				}
				else{
					dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]);
				}
			}
		}	
		*/ 
		printf("%d\n",dp[len1][len2]);
	}
} 



poj2250

和模板差不多的,就是从比较字母变成了比较单词。处理一下输入就好了

设置一个二维数组存


另外这个是一个记录路径的题。

用map[i][j]=1,2,3分别表示dp[i][j]的3个来源:dp[i-1][j-1];dp[i-1][j];dp[i][j-1];



然而样例能过,官网WA……TT也不知道哪里错了

还请看到的大神指点迷津

先把代码发到这里…发现错误是啥了我再来改TT

小伙伴们帮帮忙TT


#include <cstdio>
#include <iostream>
#include <stack>
using namespace std;
stack<string> sta;
char s[105][35],ss[105][35];
int dp[105][105];
int map[105][105],len1,len2;
//记录dp[i][j]来源路径
//1. dp[i-1][j-1],
//2. dp[i-1][j],
//3. dp[i][j-1]
void LCS(){
	memset(dp,0,sizeof(dp));
	memset(map,0,sizeof(dp));
	for(int i=1;i<=len1;i++){
		for(int j=1;j<=len2;j++){
			if(strcmp(s[i-1],ss[j-1])==0){
				dp[i][j]=dp[i-1][j-1]+1;
				map[i][j]=1;
			}
			else if(dp[i-1][j]>dp[i][j-1]){
				dp[i][j]=dp[i-1][j];
				map[i][j]=2;	
			}
			else {
				dp[i][j]=dp[i][j-1];
				map[i][j]=3;
			}			
		}
	}
	
	//测试输出:
	/* 
	for(int i=0;i<=len1;i++){
		for(int j=0;j<=len2;j++){
			printf("%d ",dp[i][j]);
		}
		printf("\n");
	}	
	printf("%d~~~~\n",dp[len1][len2]);		
	for(int i=0;i<=len1;i++){
		for(int j=0;j<=len2;j++){
			printf("%d ",map[i][j]);
		}
		printf("\n");
	}
	*/
}


void Print(){
	for(int i=len1;i>=1;){
		for(int j=len2;j>=1;){
			if(map[i][j]==1){
			//	printf("%s ",s[i-1]);   这样是正序输出的,所以需要存到栈里面
			 string str=s[i-1];
			 sta.push(str);
				i--;j--;
			}
			else if(map[i][j]==2){
				i--;
			}
			else{
				j--;
			}
		}
	} 
	while(!sta.empty()){
		printf("%s ",sta.top().c_str());
		sta.pop();
	}
	printf("\n");
}


int main(){
	while(scanf("%s",s[0])!=EOF){
		while(!sta.empty()){
			sta.pop();
		}
		
		int k=1;
		while(scanf("%s",s[k])!=EOF){
			if(s[k][0]=='#')break;
			k++;
		}
		len1=k;
		k=0;
		while(scanf("%s",ss[k])!=EOF){
			if(ss[k][0]=='#')break;
			k++;
		}
		len2=k;
		LCS();
	
		Print();
		memset(s,'\0',sizeof(s));
		memset(ss,'\0',sizeof(ss));
	} 
	return 0;
} 








Poj1159

Palindrome
Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 58763 Accepted: 20419

Description

A palindrome is a symmetrical string, that is, a string read identically from left to right as well as from right to left. You are to write a program which, given a string, determines the minimal number of characters to be inserted into the string in order to obtain a palindrome. 

As an example, by inserting 2 characters, the string "Ab3bd" can be transformed into a palindrome ("dAb3bAd" or "Adb3bdA"). However, inserting fewer than 2 characters does not produce a palindrome. 

Input

Your program is to read from standard input. The first line contains one integer: the length of the input string N, 3 <= N <= 5000. The second line contains one string with length N. The string is formed from uppercase letters from 'A' to 'Z', lowercase letters from 'a' to 'z' and digits from '0' to '9'. Uppercase and lowercase letters are to be considered distinct.

Output

Your program is to write to standard output. The first line contains one integer, which is the desired minimal number.

Sample Input

5
Ab3bd

Sample Output

2


题意: 输出至少需要往序列中加入多少个字母才能使序列变长回文串

实际上就是n-正序逆序的最长子序列

开始想着直接输入的时候是前一半和后一半字符串的最长子序列,但是输入总有问题…就改成尝试网上的正逆串比较了~


dp[maxn][maxn]

当maxn太大时,会超出内存限制,需要改装成滚动数组【常在dp中使用】因为后面的结果就仅仅依赖于前面最近的一次结果。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define maxn 5005
using namespace std;

char S[maxn],T[maxn];
int dp[2][maxn];
int main(){
	int n;
//	freopen("1159.txt","r",stdin);
	scanf("%d",&n);
	scanf("%s",S);
	for(int i=0;i<n;i++){
		T[n-1-i]=S[i];
	}
	int e=0; 
	
	memset(dp,0,sizeof(dp));
	for(int i=0;i<n;i++){
		e=1-e;
		for(int j=0;j<n;j++){
			if(S[i]==T[j]){
				dp[e][j+1]=dp[1-e][j]+1;
			}
			else{
				dp[e][j+1]=max(dp[e][j],dp[1-e][j+1]);
			}
		}
	}
	cout<<n-dp[e][n]<<endl;
	
	return 0;
}




超水的本人,读取一半字符串的代码在下面…发现问题的麻烦联系我TT…始终不能正确写入T……


	scanf("%d",&n);
	int i;
	getchar();
	for( i=0;i<n/2;i++){
		scanf("%c",&S[i]);
	}
	
	if(n%2==0){
		scanf("%c",&S[++i]);
	} 
	else{getchar();}
	//cout<<"i="<<i<<endl;
	int j;
	for( j=0;i+j<n-1;j++){
		scanf("%d",&T[j]);
	}









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值