最长公共子序列 HDU 1159/1080/1503

hdu 1159 

题意:求两个串的最长公共子序列

#include<iostream>
#include<string.h>
#include<stdio.h>
#define size 1000
using namespace std;
char str1[size],str2[size];
int dp[size][size];
int main()
{
    int len1,len2;
    while(scanf("%s",str1)!=EOF)
    {
        getchar();
        scanf("%s",str2);
        len1=strlen(str1);
        len2=strlen(str2);
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=len1;i++)
        {
            for(int j=1;j<=len2;j++)
            {
                if(str1[i-1]==str2[j-1] )
                {
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                else
                {
                    dp[i][j]= max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
        printf("%d\n",dp[len1][len2]);
    }
    return 0;
}
hdu 1080

题意:两个字符串,每个字符串中都可以插入'-',保证最后两串的长度相等,之后让两串对齐,两串相同位置的字母组成的字符匹配有一个值,问这些值的和最大是多少

#include<iostream>
#include<string.h>
#include<stdio.h>
#define INF 0x3fffffff
using namespace std;
int tmp[5][5]=
{
   {5,-1,-2,-1,-3},
   {-1,5,-3,-2,-4},
   {-2,-3,5,-2,-2},
   {-1,-2,-2,5,-1},
   {-3,-4,-2,-1,0}
};

int tt(char c)
{
    if(c=='A')  return 0;
    else if(c=='C') return 1;
    else if(c=='G') return 2;
    else return 3;
}
int dp[110][110];
int main()
{
    int len1,len2;
    char str[110];
    int a[110],b[110];
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %s",&len1,str);
        int i,j;
        for(i=0;i<len1;i++)
        a[i]=tt(str[i]);
        scanf("%d %s",&len2,str);
        for(i=0;i<len2;i++)
        b[i]=tt(str[i]);
//        for(i=0;i<len1;i++) cout<<a[i]<<" ";cout<<endl;
//        for(i=0;i<len2;i++) cout<<b[i]<<" ";cout<<endl;
        for(i=0;i<=len1;i++)
        //给dp初始化的原因是tmp[][]中包含负数,且结果是取最大值,所以显然不能直接初始化为0
        {
            for(j=0;j<=len2;j++)  dp[i][j]=-INF;
        }
        dp[0][0]=0; //为下面的边界初始化有效
        //如果str1的前i个字符都不能与str2中合理匹配,则它们与'_'相匹配,而“匹配值”逐次累加。
        for(i=1;i<=len1;i++)  dp[i][0]=dp[i-1][0]+tmp[a[i-1]][4]; //之前这里写成了dp[][]+=tmp[][]
        for(j=1;j<=len2;j++)  dp[0][j]=dp[0][j-1]+tmp[4][b[j-1]];
        //dp[i][j]代表str1前i-1个与str2前j-1个“匹配值”的和。
        for(i=1;i<=len1;i++)
        {
            for(j=1;j<=len2;j++)
            {
                dp[i][j]=max(dp[i][j],//不可去除,可能是上面的边界情况
                             max(dp[i-1][j-1]+tmp[a[i-1]][b[j-1]],//若str1[i-1]==str[j-1]
                             max(dp[i-1][j]+tmp[a[i-1]][4],//可能str1[i-1]与'—'匹配,结果最大
                                 dp[i][j-1]+tmp[4][b[j-1]])));//可能str2[j-1]与'-'匹配结果最大
            }
        }
        printf("%d\n",dp[len1][len2]);
    }
    return 0;
}

hdu 1503

题意:

给你两个单词,然后你要把两个单词拼接成一个新单词,使得新单词的子序列中包含两个单词,并且要使这个新单词最短

基本思路:

求最长公共子序列,令这个序列只输出一次就可以使新单词最短

记录路径:

增加二维数组road记录状态转移路径

road[i][j] = 0 表示road[i][j]由road[i-1][j-1]转移过来,即a[i-1]与b[j-1]属于最长公共子序列中的元素,扫描路径时将hash[i-1]赋值为j-1表示a串的i-1匹配b串的j-1【其中hash初始时全为-1】

road[i][j] = 1 表示road[i][j]由road[i-1][j]转移过来

road[i][j] = 2 表示road[i][j]由road[i][j-1]转移过来

输出答案:

先设置start变量【表示b串的当前位置】,扫描a串

①当对于a[i]有hash[i]==-1,说明a[i]不是最长公共子序列中的元素,直接输出并且continue;

②否则b串输出从start到hash[i]的值,因为a[i]跟b[hash[i]]匹配嘛,所以输出b[hash[i]]就不用输出a[i]勒,然后start变为hash[i] + 1;

#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
int main()
{
    char str1[110],str2[110];
    int hash[110];
    int dp[110][110];
    int roat[110][110];
    int len1,len2;
    int i,j;
    while(scanf("%s %s",str1,str2)!=EOF)
    {
        memset(dp,0,sizeof(dp));
        memset(hash,-1,sizeof(hash));
        memset(roat,-1,sizeof(roat));
        len1=strlen(str1);
        len2=strlen(str2);
        for(i=1;i<=len1;i++)
        {
            for(j=1;j<=len2;j++)
            {
                if(str1[i-1]==str2[j-1])
                {
                    dp[i][j]=dp[i-1][j-1]+1;
                    roat[i][j]=0; //cout<<"**********"<<endl;
                }
                else
                {
                    if(dp[i-1][j]<dp[i][j-1])
                    {
                        dp[i][j]=dp[i][j-1];
                        roat[i][j]=1;
                    }
                    else
                    {
                        dp[i][j]=dp[i-1][j];
                        roat[i][j]=2;
                    }
                }
            }
        }
        //由roat[][]数组找路径,=0表示由[i-1][j-1]转换来(str1[i-1]=str2[j-1])
        //=1 =2由上可推得
            i=len1;j=len2;
            while(roat[i][j]!=-1)
            {
                if(roat[i][j]==0)
                {
                    i--;j--;
                    hash[i]=j;
                }
                else if(roat[i][j]==1)
                j--;
                else i--;
            }
          //  for(int i=1;i<=len1;i++)  cout<<"hash["<<i-1<<"]= "<<hash[i-1]<<endl;
          //输出组合词
            int s=0;
            for(i=0;i<len1;i++)
            {
                while(hash[i]!=-1 && i<len1)
                {
                    for(;s<=hash[i];s++)
                    {
                        printf("%c",str2[s]);
                    }
                    i++;
                }
                if(hash[i]==-1 && i<len1)
                printf("%c",str1[i]);
            }
            for(;s<len2;s++)
            {
                printf("%c",str2[s]);
            }
            printf("\n");
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值