最长公共子序列——动态规划
首先,说明一下子序列的定义……
一个序列A={a1,a2,a3,...,an},从中删除任意若干项,剩余的序列叫A的一个子序列。
很明显(并不明显……),子序列……并不需要元素是连续的……(一开始的时候思维总是以为元素是连续的,好傻啊……)
然后是公共子序列……
如果C是A的子序列,也是B的子序列,那么C是A和B的公共子序列……
公共子序列一般不止一个,最长的那个就是最长公共子序列,当然也可能不止一个……
煮个栗子……
A={1,3,6,9,5,4,8,7},B={1,6,3,4,5,7}
{1,4,7}是A和B的公共子序列
{1,3,4,7}是A和B的最长公共子序列
好了,说明的部分就到这,接下来,进入解决问题的部分……
给出序列A={a1,a2,a3...an},B={b1,b2,b3...bn}
我们用lcs[i][j]来表示A的前i项和B的前j项的最长公共子序列的长度
flag[i][j]表示这一点是由哪点继承来的,然后,开始搜索……
如果……
(1)A[i]==B[j]
那么就代表lcs[i][j]的最后一项一定是A[i],就是在lcs[i-1][j-1]接上A[i],也就是lcs[i][j]=lcs[i-1][j-1]+1,并记录flag[i][j]
(2)A[i]!=B[j]
那么lcs[i][j]=max(lcs[i-1][j-1],lcs[i][j-1]),因为既然接不上,那就继承一个长一点的留着呗,不要忘记了flag[i][j]的数字是不同的亲~
可能我说的比较简略,上一张比较好理解的图……
可能第一次看会很凌乱,仔细看吧亲……
最后按flag打出来就好了~
最后上代码
#include<stdio.h>
#include<string.h>
int lcs[1005][1005];
int flag[1005][1005];
char a[1005],b[1005];
void findlcs(char a[],char b[],int lena,int lenb){
int i,j;
for(i=1;i<=lena;i++)
for(j=1;j<=lenb;j++){
if(a[i-1]==b[j-1]){
lcs[i][j]=lcs[i-1][j-1]+1;
flag[i][j]=1;
}
else{
if(lcs[i][j-1]>lcs[i-1][j]){
lcs[i][j]=lcs[i][j-1];
flag[i][j]=2;
}
else{
lcs[i][j]=lcs[i-1][j];
flag[i][j]=3;
}
}
}
}
void printlcs(char a[],char b[],int lena,int lenb){
int i=lena;
int j=lenb;
int len=0;
int rec[1050];
while(i>0&&j>0){
if(flag[i][j]==1){
rec[len++]=a[i-1];
i--,j--;
}
else if(flag[i][j]==2) j--;
else if(flag[i][j]==3) i--;
}
len--;
while(len>=0){
printf("%c",rec[len--]);
}
printf("\n");
}
int main(){
while(~scanf("%s%s",a,b)){
int lena=strlen(a);
int lenb=strlen(b);
findlcs(a,b,lena,lenb);
printlcs(a,b,lena,lenb);
memset(flag,0,sizeof(flag));
memset(lcs,0,sizeof(lcs));
}
return 0;
}
上两个具体题目(题目出自buaacoding.cn)
Bamboo and the Ancient Spell
时间限制: 1000 ms 内存限制: 65536 kb
Problem description
The Hogwarts has set up a new course recently, giving you the ancient spells and requiring you to translate them into the modern ones. Every two ancient spells encode one modern spell.
Wizards and witches just point the ancient spells with the magic wands and speak loudly "THE LONGEST COMMON SUBSEQUENCE !", and they'll get the modern spell required:)
Ok, stop trying this muggles...Though we can't get the answers as quickly as they do, we can still decode them by coding :)
So Bamboo will give you a lot of ancient spells( two in a group),and hope you just return the LENGTH of the modern spell.
Attention please! The ancient spells may include strange characters '#' and '?'. They are too old to know their exact meanings, so we can just think that '#'s can not match any character while finding the answer, but '?' can match any character in the other ancient spell except '#'.
Input
The input file consists of several test cases.
Each test case includes 2 lines, one ancient spell per line.
The length of per ancient spell will less than 100 characters,and all characters are in upper case.
Output
For each case, output one line: the length of the modern spell.
sample input
ABCDE
ZBD
AC#EB
C#BFG
ACE#?
KIKI#A
???
?#?
Sample output
2
2
1
2
A TIP
emmmm, Can you guess what 'spell' means here? And what about the modern spells? Pay attention to what wizards and witches say when they use magic.
Another TIP
In case 2, '#' can not match any character, just like it doesn't exist, so the modern spell is "CB".
In case 3, '?' can match any character except '#', which means '?' can be seen as ‘A~Z' and '?' of course.
题目分析
这道题是最长公共子序列问题的一个简单改编,本质上还是一个最长公共子序列问题,只不过要在判断字符相等时加一些条件。
用lcs[i][j]表示串a的前i个字符与串b的前j个字符的最长公共子序列长度
那么,当a[i]与b[j]匹配的时候
lcs[i][j]=lcs[i-1][j-1]+1
当a[i]与b[j]不匹配的时候
lcs[i][j]=max{lcs[i-1][j-1],lcs[i][j-1]}
示例代码
#include<stdio.h>
#include<string>
#include<iostream>
#include<cstring>
using namespace std;
int lcs[1005][1005];
char a[1005],b[1005];
int len_a,len_b;
void find_lcs(char a[],char b[],int len_a,int len_b)
{
int i,j;
for(i=1;i<=len_a;i++)
for(j=1;j<=len_b;j++){
if((a[i-1]!='#'&&b[j-1]!='#')&&(a[i-1]==b[j-1]||a[i-1]=='?'||b[j-1]=='?')){
lcs[i][j]=lcs[i-1][j-1]+1;
}
else {
if(lcs[i][j-1]>lcs[i-1][j]){
lcs[i][j]=lcs[i][j-1];
}
else{
lcs[i][j]=lcs[i-1][j];
}
}
}
}
int main()
{
while(scanf("%s %s",&a,&b)!=EOF)
{
len_a=strlen(a);
len_b=strlen(b);
memset(lcs,0,sizeof(lcs));
find_lcs(a,b,len_a,len_b);
printf("%d\n",lcs[len_a][len_b]);
}
}