UVA10723-Cyborg Genes

大概题意:
给你两个字符串m,n让你找到一个让这两个字符串成为其子串的最短字符串str,并且输出这种字符串有多少种构建方式

这题属于dp,并且涉及到他们最大公共子序列,记为s

首先能想到的是s同样是目标字符串str的子串,那么现在把s固定,把mn串的其他字符相对顺序不变地插入s中便能得到最大最短目标字符串,这样便能求出最短长度

下面如何求有多少种构造数呢

上面说到固定s,然后让mn其他字符按照相对顺序不变插入s中各个区域(s中字母把s分成若干个区域)

下面为演示 (x为其他未能匹配的字符,@只是占位表示开头)
m: @(x1)xxxaxxxxbxxxcxxx
n: @(x2)xxxaxxxbxcxx
s =@ _(1区域) _a _(2区域)__b _(3区域)_ c;

第一个匹配@(只是形式上匹配方便理解)之前有k种构造
第二次m中的x1和n匹配,因为abc已经是最长公共子序列了,所以在n中a之前匹配不到相同的.首先匹配n@,n@之前成功匹配的字符为0,x1之前成功匹配为1(即是@),这表示他们肯定位于s中的不同区域,那么他们的相对顺序被固定了,构造数不增加

当匹配到x2时候,x1和x2位于s中的相同区域(匹配符@和a之间),所以x1和x2能够在@和a之间随意排序;
x1若放到x2之后相当于现在的构造数加上了dp[x1-1][x2]表示x1-1和x2之前所合成的串所产生多少种构造.若放到x2之前,相当于构造数加上了dp[x1][x2-1],依次这样类似的匹配下去,而匹配到na之后x1再匹配由于和他们处于区域不同,所以对构造数不产生影响,也就是加0…..x1匹配完之后匹配m中(x1+1),轮回下去直至不能匹配

从上面的模拟匹配过程中,发现可以这样建立状态dp[i][j]表示m,n中前i和j的所合成的串中有多少种构造方法,
而状态转移的条件是当前i和j能够位于s中的相同区域也就是i,j之前的匹配字符数相等(这里包含了LCS求解)
相同区域如果ij不同的话,构造数状态dp[i][j]=dp[i-1][j]+dp[i][j-1];如果ij相同表示进入新的区域.如上区域如果不同的话构造数不变
PS:这里很显然需要另外一个数组记录i,j的匹配情况但是由于会出现1配多的情况,而这个数组如同lcs一样需要二维的

如:@xx1xxax2xxxxxxbxxxcxxx
@xx3xxax4xax5xxbxcxx

这里a一个配了多个,1->3,4 可以属于一个区域 2->4,5可以属于一个区域,这里如果只用一维记录能匹配到多少字符会出错,因为1之前只有1个可以匹配,4之前有两个可以匹配,按照上面所说这里属于不同区域不能匹配,但是1以前的字符和4以前字符匹配的话只有一个匹配,所以用数组l[i][j]记录i和j以前能匹配到最大的

yy待续中
此文出错的几率为99.99%
到现在还没搞懂的是上述情况,l[i][j]是如何让情况都取到的

下面是别人很抽象的但是容易理解的想法
若a[i]=b[j],那么c[i][j]=c[i-1][j-1],即a串前i-1个元素和b串前j-1个元素得到的组合串的末尾加上一个相同的元素a[i],那么得到的新的组合串的个数还是和之前的组合串的个数一样

若a[i]!=b[j], l[i][j]=max { l[i-1][j] , l[i][j-1]}

若l[i-1][j]>l[i][j-1],那说明从l[i-1][j]这种状态开始构建才能得到最终的LCS同时最终的组合串才不能漏掉共有的元素,所以c[i][i]=c[i-1][j],即在a串i-1个元素和b串j个元素组成的组合串的后面加上a[i],那么得到的新的组合串的个数和之前的组合串的个数是相同的

若l[i][j-1]>l[i-1][j],道理和上面是一样的,所以c[i][j]=c[i][j-1],相当于在之前的组合串后面加上元素b[j],得到新的组合串的个数不变

//
//  Created by Zeroxf on 2015-08-12-13.56
//  Copyright: (c) 2015 Zeroxf. All rights reserved
//
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
using namespace std;
const int maxn = 40;
char a[maxn],b[maxn];
int l[maxn][maxn],lena,lenb;
long long c[maxn][maxn];
int main(){
#ifdef LOCAL
    freopen("in.txt","r",stdin);
   // freopen("out.txt","w",stdout);
 #endif
    int T,i,j;
    scanf("%d",&T);getchar();
    for(int kase=1;kase<=T;kase++)
    {
        gets(a+1);gets(b+1);
        lena=strlen(a+1);lenb=strlen(b+1);
        memset(l,0,sizeof l);
        memset(c,0,sizeof c);
        for(i=0;i<=lena;i++) c[i][0]=1;
        for(i=0;i<=lenb;i++) c[0][i]=1;
        for(i=1;i<=lena;i++)
        {
            for(j=1;j<=lenb;j++)
            {
                if(a[i]==b[j])
                {
                    l[i][j]=l[i-1][j-1]+1;
                    c[i][j]=c[i-1][j-1];
                }
                else
                {
                    if(l[i][j-1]>l[i-1][j])
                    {
                        l[i][j]=l[i][j-1];
                        c[i][j]=c[i][j-1];
                    }
                    else if(l[i-1][j]>l[i][j-1])
                    {
                        l[i][j]=l[i-1][j];
                        c[i][j]=c[i-1][j];
                    }
                    else
                    {
                        l[i][j]=l[i-1][j];
                        c[i][j]=c[i-1][j]+c[i][j-1];
                    }
                }
            }
        }
        printf("Case #%d: %d %lld\n",kase,lena+lenb-l[lena][lenb],c[lena][lenb]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值