双向bfs搜索-字串变换

https://www.acwing.com/problem/content/description/192/

已知有两个字串 AA, BB 及一组字串变换的规则(至多 66 个规则):

A1→B1A1→B1

A2→B2A2→B2

规则的含义为:在 AA 中的子串 A1A1 可以变换为 B1B1、A2A2 可以变换为 B2…B2…。

例如:AA=abcd BB=xyz

变换规则为:

abc →→ xu ud →→ y y →→ yz

则此时,AA 可以经过一系列的变换变为 BB,其变换的过程为:

abcd →→ xud →→ xy →→ xyz

共进行了三次变换,使得 AA 变换为 BB。

输入格式

输入格式如下:

AA BB
A1A1 B1B1
A2A2 B2B2
… …

第一行是两个给定的字符串 AA 和 BB。

接下来若干行,每行描述一组字串变换的规则。

所有字符串长度的上限为 2020。

输出格式

若在 1010 步(包含 1010 步)以内能将 AA 变换为 BB ,则输出最少的变换步数;否则输出 NO ANSWER!

输入样例:

abcd xyz
abc xu
ud y
y yz

输出样例:

3

 

这道题如果用简单的搜索的话,很有可能会超时,为了提高效率,用双向搜索来解决这道题,其中双向搜索最重要的一点就是,用两个队列来分别从两端进行搜索,在用两个map容器分别记录走到搜索点所用的步数,即可。

另外拓展一些关于字符串的函数,

s.substr(begin, end) 

返回一个string,包含s中从begin开始到end的字符串,或s中从begin开始的的n个字符的拷贝(begin的默认值是0,n的默认值是s.size() - pos,即不加参数会默认拷贝整个s)

#include<bits/stdc++.h>
#include<queue>
#include<map>
using namespace std;

int n=0;//记录规则个数 
string a[7],b[7];//分别存放变换前后的规则 
string A,B;
queue<string> qa,qb; //分别从A,B开始bfs的队列 
map<string,int> da,db;//分别记录到起点A,终点B的距离(搜索的步数 ) 

int bfs(){
	if(A==B){ //这步必不可少,若没有这步就会tle, 下面没有判断当A==B时的情况 
		return 0;
	}
	qa.push(A),qb.push(B);
	da[A]=0;
	db[B]=0; 
	while(!qa.empty()&&!qb.empty()){//若双向队列有一方为空,说明有一端已经遍历完成,但仍未变成目标字符,即不可能实现 
		if(qa.size()<=qb.size()){// 哪个方向的队列更小,它的拓展空间更小,先拓展哪一方向 
		//	int step=0;
			string t=qa.front();
			qa.pop();
			if(da[t]>10||db[t]>10) {  //不满足条件 
				return 11;
			}
			for(int i=0;i<t.size();i++){  // eg.当t=abcd时,从a开始判断有没有可变换的规则 
				for(int j=0;j<n;j++){//共n个规则,每次遍历一遍 
					if(t.substr(i,a[j].size())==a[j]){
						string temp=t.substr(0,i)+b[j]+t.substr(i+a[j].size(),t.size());
						if(db.count(temp)) return db[temp]+da[t]+1;//说明另一方向已经遍历过,两个方向碰头,返回两着距离之和加一,即为最短距离 
						if(da.count(temp)) continue;//已经遍历过 
						qa.push(temp);//没有遍历过这种情况,该方向的队列 
						da[temp]=da[t]+1;//temp离起点的距离为t到七点的距离加一						
					}
				}
			}
		}
		else{
			string t=qb.front();
			qb.pop();
			for(int i=0;i<t.size();i++){
				for(int j=0;j<n;j++){
					if(t.substr(i,b[j].size())==b[j]){
						string temp=t.substr(0,i)+a[j]+t.substr(i+b[j].size(),t.size());
						if(da.count(temp)) return db[t]+da[temp]+1;
						if(db.count(temp)) continue;
						qb.push(temp);
						db[temp]=db[t]+1;						
					}
				}
			}
		}
		
	}
	return 11;//不可能实现的返回11,超过10,输出NO ANSWER! 
}

int main(){
	cin>>A>>B;
	while(cin>>a[n]>>b[n]) n++; // 在不知道规则个数的情况下采用这耳中输入方式 
	int p=bfs();  //一般情况下,bfs都是返回void类型,但是这道题返回int类型比较方便处理结果 
	if(p>10) cout<<"NO ANSWER!"<<endl;
	else cout<<p<<endl;
	
	
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值