1032 字串变换

1032 字串变换

开学还有两天,我却没有努力补作业,在这里刷题…
开学之前练练一些78的搜索剪枝、迭代加深,启发式等
还有一些图论还没有结业,今天全部结束,然后明天搞搞作业准备开学
给定两个字符串
通过一系列的操作进行更改,貌似和搜索并没有关系?
解决这道题可以用迭代加深搜索,正常情况,dfs会超时,可以用迭代加深,就好了
这是第一种做法,做完这个我再来练习剪枝优化,毕竟我是来练习剪枝的
或者,双向广搜

迭代

普通的dfs要一路搜索到底,一路走南墙,可能会在错误答案上一路走到黑,从而浪费大量的时间
迭代加深搜索是什么
就是每次探测一个可能的答案,超过答案就会直接终止搜索,通过枚举答案一层一层的搜索,这样就既有了深搜的递归形式,又有了广搜的一层一层枚举,不会浪费时间,所以迭代加深有价值BDFS,结合了两个搜索的优点
算是比较模板了,需要弄多个剪枝优化

-------------重新---------------------------
嘿嘿嘿
我又来了兄弟们
这个题不就是一个递归搜索吗
显然
我忽略了这个题的本质
我忽略了这个题的颜色
所以说,我总感觉奥赛这个东西其实就是为时间复杂度来服务的
要是没有什么时间复杂度的限制,谁去搞这么麻烦的算法
先说一下题意
题意:
其实就是不停的进行转换
给定两个字符串 A串B串
然后给定几个等价交换的规则,A串换成B串的规则
最后问最快A ——>B的步骤是多少
分析:
典型的搜索
很明显
首先,循环输入,之前已经接触过了,存储到数组里面
双方向的搜索,省时间,每个搜索只需要搞一半

#include<iostream>
#include<cstdio>
#include<string>
#include<algorithm>
#include<map>
using namespace std;
map <string,bool> ed;
map <string,int> st;
string a,b;
string change[30][3];
int t=1,k=2;
int ans=999999;
void dfs(string now,int step)
{
	if(step>k)return;//超出步数直接终止,小小的优化 
	if(now==b)
	{
	
		ans=min(ans,step);//搜索到头,比较答案 
		return;
	}
	if(ed[now])//判断重复状态 
	{
		if(step>=st[now])return; 
	}
	ed[now]=1;st[now]=step;//记录层数 
	int loc=0;string changed;
	for(int i=1;i<=t;++i)
	{//找所有的字符串 
		loc=-1;//记录位置 
		 while(1)
		 {//找到了 
		 	loc=now.find(change[i][0],loc+1);
		 	if(loc==-1)break;//查找失败 
		 	changed=now;
		 	changed.erase(loc,change[i][0].size());
		 	changed.insert(loc,change[i][1]);
		 	dfs(changed,step+1);//查找成功,更改插入 
		 }
	}
	return;
}
int main()
{
	cin>>a>>b;
	while(cin>>change[t][0]>>change[t][1])
	{
		t++;//记录个数 
	}
	t--;
	while(ans==999999)//一直无解 
	{
		dfs(a,0);
		ed.clear();
		st.clear();
		k++;
		if(k==11)break;//记录搜索次数,小剪枝 
	}
	
	if(ans==999999)
	{
		cout<<"NO ANSWER!"<<endl;//答案错误 
		return 0;
	}
	cout<<ans<<endl;//最后输出答案 
	return 0;
} 

双向广搜

首先假设我们从起点搜索到终点,直到搜索到s点的时候结束,然后我们再将冗余的部分减去
我们再反向搜索一下,将终点作为起点,起点作为终点搜索一下,再将冗余的部分减去
最后
从一边的节点一定可以顺着另外一边的搜索枝到达终点
那么就实现了双向搜索了
在这里插入图片描述

在这个题中,可以使用map去实现查找现在我们搜索的点是否出现过,假如是对面的,那么一位双向的BFS碰面了,结束就好
每次出队的时候,只能够出上一层的搜索节点,不能使用这一层的搜索节点,保证两边一步一步的走
所以map对应的层数应该减去1

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<map>
#include<queue> 
using namespace std;
string a[10],b[10],s,t;
int n=1;
map <string,int> A,B;//pair< string , step + 1 >
queue <string> A_,B_; 
inline void initialization()
{
	cin>>s>>t;
	while(cin>>a[n]>>b[n])n++;
}
inline int bfs()
{
	int step=0;
	A_.push(s);//开始状态
	A[s]=0;//开始状态的层数 
	B_.push(t);//开始状态
	B[t]=0;//结束状态的层数 
	string s,s2;
	while(++step<=5)//一边是10步之内,那么两边一起走就是5步之内 
	{
		while(A[A_.front()]==step-1)//保证是上一层的 
		{
			s=A_.front();
			A_.pop();
			for(int i=1;i<=n;i++)//对于每一个转换方案遍历 
			{
				unsigned int pos=0;//遍历开始搜索的节点,结合string::find( key_string , starting ) 
				while(pos<s.length())
				{
					if(s.find(a[i],pos)==s.npos)break;//如果找不到了 
					s2=s;
					s2.replace(s2.find(a[i],pos),a[i].length(),b[i]);//replace( starting , length , substitution )
					if(A.find(s2)!=A.end())//这棵树里面之前出现过 
					{
						pos++;
						continue;
					}
					if(B.find(s2)!=B.end())return step*2-1;//对面的搜索树里面出现过,由于是上面先走,所以-1 
					A_.push(s2);//入队 
					A[s2]=step;
					pos++;
				}
			}
		}
		while(B[B_.front()]==step-1)//保证是上一层的 
		{
			s=B_.front();
			B_.pop();
			for(int i=1;i<=n;i++)//对于每一个转换方案遍历 
			{
				unsigned int pos=0;//遍历开始搜索的节点,结合string::find( key_string , starting ) 
				while(pos<s.length())
				{
					if(s.find(b[i],pos)==s.npos)break;//如果找不到了 
					s2=s;
					s2.replace(s2.find(b[i],pos),b[i].length(),a[i]);//replace( starting , length , substitution )
					if(B.find(s2)!=B.end())//这棵树里面之前出现过 
					{
						pos++;
						continue;
					}
					if(A.find(s2)!=A.end())return step*2;//对面的搜索树里面出现过,由于是上面先走,所以-1 
					B_.push(s2);//入队 
					B[s2]=step;
					pos++;
				}
			}
		}
	}
	return -1;
}
int main()
{
	initialization();
	int ans=bfs();
	if(ans==-1)
	printf("NO ANSWER!");
	else printf("%d",ans);
	return 0;
}

在这里插入图片描述
事实证明双向广搜时间更快,快的不是一点两点

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值