hdu1501 zipper【记忆化搜索】【动态规划】

题目大意:

给三个字符串A,B,C,问A和B能否按顺序组成C;

解题思路:

冲突关键在于如果C中某字符刚好都等于枚举到的A中字符和B中字符该怎么转移。

法一:记忆化搜索。
dp[i][j]表示A枚举到i,B枚举到j时能否匹配成功。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#define ll long long
using namespace std;

int getint() 
{
    int i=0,f=1;char c;
    for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
    if(c=='-')c=getchar(),f=-1;
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=205;
int T,t1n,t2n,sn,dp[N][N];
char t1[N],t2[N],s[N<<1];

bool dfs(int x,int y,int z)
{
    if(x==t1n+1&&y==t2n+1&&z==sn+1)return true;
    if(dp[x][y]!=-1)return dp[x][y];
    if(s[z]==t1[x]&&s[z]==t2[y])return dp[x][y]=dfs(x+1,y,z+1)||dfs(x,y+1,z+1);
    else if(s[z]==t1[x])return dp[x][y]=dfs(x+1,y,z+1);
    else if(s[z]==t2[y])return dp[x][y]=dfs(x,y+1,z+1);
    return dp[x][y]=false;
}

int main()
{
    //freopen("lx.in","r",stdin);
    T=getint();
    for(int k=1;k<=T;k++)
    {
        memset(dp,-1,sizeof(dp));
        scanf("%s%s%s",t1+1,t2+1,s+1);
        t1n=strlen(t1+1);
        t2n=strlen(t2+1);
        sn=strlen(s+1);
        printf("Data set %d: ",k);
        if(!dfs(1,1,1))puts("no");
        else puts("yes");
    }
    return 0;
}

法二:动态规划。
最优子结构分析:如果A、B可以组成C,那么C最后一个字母必定是 A 或 C 的最后一个字母组成。
C去除除最后一位,就变成是否可以求出 A-1和B 或者 A与B-1 与 是否可以构成 C-1。。。
状态转移方程: 用dp[i][j] 表示 表示A前 i 为 和B 前j 位是否可以组成 C的前i+j位        
 dp[i][j]= (dp[i-1][j]&&(a[i]==c[i+j]))||(dp[i][j-1]&&(b[j]==c[i+j]))
注意对边界的处理,初始化条件为主串长度的字符相同即为1,否则为0。
就是转移方程中i=0或j=0的情况,但i-1=-1会炸。
其意义是为判断整个A或B是C的前缀。

#include <stdio.h>  
#include <algorithm>  
#include <string.h>  
#include <string>  
#include <iostream>  

using namespace std;  

const int N = 205;  
const int INF = 1e8;  

char str1[N], str2[N], str[N * 2];  
int dp[N][N];  

int main()  
{   
    int t, n, Case = 1;  
    scanf("%d", &t);  
    while(t --)  
    {  
        scanf("%s%s%s", str1 + 1, str2 + 1, str + 1);  
        str[0] = str1[0] = str2[0] = '0';  
   //     memset(dp, 0, sizeof(dp));  
        for(int i = 1; i < strlen(str1); i ++)  
        {  
            if(str1[i] == str[i]) dp[i][0] = 1;  
            else dp[i][0] = 0;  
        }  
        for(int i = 1; i < strlen(str2); i ++)  
        {  
            if(str2[i] == str[i]) dp[0][i] = 1;  
            else dp[0][i] = 0;  
        }  
        for(int i = 1; i < strlen(str1); i ++)  
        {  
            for(int j = 1; j < strlen(str2); j ++)  
            {  
                dp[i][j] = ((dp[i - 1][j] && str1[i] == str[i + j]) || (dp[i][j - 1] && str2[j] == str[i + j]));  
            }  
        }  
        if(dp[strlen(str1) - 1][strlen(str2) - 1]) printf("Data set %d: yes\n", Case ++);  
        else printf("Data set %d: no\n", Case ++);  
    }  
    return 0;  
}  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值