POJ 2192 Zipper [dfs+剪枝 || dp]

题意:

给定三个字符串,如果前两个可以拼装成第三个,则输出yes,否则输出no。


思路:

1:

DFS:

最直观的思路是可以用dfs去搜,从第三个字符串为目标串去dfs(0,0,0)。

如果在第一个串里字符匹配,则dfs(i+1,j,k)。这种dfs本质所在搜一刻二叉树。

但是我觉得这种ac了说明数据弱,否则因为这种dfs需要回溯,很容易超时。

不过ac也是看了discuss里面的一个神奇的剪枝才ac的。


2:

DP:

从dfs深搜的过程中其实就可以发现规律。

我也是在写dfs的过程中想到其实可以用记忆化dp,因为记忆化搜索我的理解就是为了消除回溯的。

dp方程在代码里有解释。

时间复杂度O(N);


挺不错的一道题目,加深了我对记忆化dp的理解。


深搜代码:

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int N=405;
int len0,len1,len2;
char str[4][N];
int dp[N+N];
//dp[i+j]=(str1[i+1] && str2[j]) || (str1[])

bool dfs(int pt0,int pt1,int pt2)
{
	if(pt2>=len2)
	  return true;
	char a=str[2][pt2];
	//	printf("%d%d%d%c\n",pt0,pt1,pt2,a);
	//	printf("%c",str[0][pt0]);	
	if(pt0<len0&&str[0][pt0]==a)
	{
		if(dfs(pt0+1,pt1,pt2+1))
		  return true;
	}
	//	printf("%c",str[1][pt1]);	
	if(pt1<len1&&str[1][pt1]==a)
	{
		if(dfs(pt0,pt1+1,pt2+1))
		  return true;
	}
	//	printf("%d%d%d%c\n",pt0,pt1,pt2,a);
	return false;
}
void solve()
{
	if(dfs(0,0,0))
	  printf("yes\n");
	else
	  printf("no\n");
}
int main()
{
	//freopen("1.txt","r",stdin);
	int T;
	scanf("%d",&T);
	for(int t=1;t<=T;t++)
	{
		memset(dp,0,sizeof(dp));
		scanf("%s%s%s",str[0],str[1],str[2]);
		len2=strlen(str[2]);
		//		printf("%s %s %s\n",str[0],str[1],str[2]);
		len0=strlen(str[0]);
		len1=strlen(str[1]);
		if(str[0][len0-1]!=str[2][len2-1]&&str[1][len1-1]!=str[2][len2-1])
		{
			
			printf("Data set %d: no\n",t);
			continue;
		}
		printf("Data set %d: ",t);
		solve();
	}
	return 0;
}



dp代码:

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int N=405;
int len0,len1,len2;
char str[4][N];
bool dp[N][N/2];
//dp[k][i]:
//1:代表第三个字符串的第k个字符由第一个字符串的前i个字符和第二个字符从的前k-i个字符构成。
//0:代表第三个字符串的第k个字符由第一个字符串的前i个字符和第二个字符从的前k-i个字符无法构成。
bool solve()
{
	if(str[0][1]==str[2][1])
	  dp[1][1]=true;
	if(str[1][1]==str[2][1])
	  dp[1][0]=true;
	for(int k=1;k<len2;k++)
	{
		int flag=0;
		for(int i=0;i<=k;i++)
		{
			int j=k-i;
			if(dp[k][i])
			{
				flag=1;
				if(str[0][i+1]==str[2][k+1])
				{
					dp[k+1][i+1]=true;
				}
				if(str[1][j+1]==str[2][k+1])
				{
					dp[k+1][i]=true;
				}
			}
		}
		if(!flag)
		  return false;
	}

	bool ans=false;
	for(int i=0;i<len2;i++)
	{
		if(dp[len2][i])
		{
			ans=true;
			break;
		}
	}
	return ans;
}
int main()
{
	int T;
	scanf("%d",&T);
	for(int t=1;t<=T;t++)
	{
		memset(dp,0,sizeof(dp));
		scanf("%s%s%s",str[0]+1,str[1]+1,str[2]+1);
		len2=strlen(str[2]+1);
		len0=strlen(str[0]+1);
		len1=strlen(str[1]+1);
		printf("Data set %d: ",t);
		if(solve())
		  printf("yes\n");
		else
		  printf("no\n");
	}
	return 0;
}

人生第一次在linux下vim编辑器写的代码,用惯了vs 的人表示gdb调试得想哭,后来索性就放弃gdb了,直接靠printf来调试,泪流满面。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值