字串变换(AcWing, NOIP2002提高组)

题目描述:

题目链接: 

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

思路:

这个题是要求“最小步数”,比较容易想到是用BFS来进行搜索,但是直接BFS的话状态数太多了,时间复杂度会到:O((LN)^{10}),其中L是字符串的长度,N是一个字符串的可能变换到的后继字符串数。肯定会超时。

因此,本题使用了“双向搜索”的技巧,i.e. 分别从起点和终点向中间进行搜索,如果搜到了“公共点”,那这个公共点就是答案。 使用双向搜索的时间复杂度是:O(2(LN)^{5})

在具体实现的时候,使用了一些实现技巧(需要注意!):

1. 优先从队列比较小的那个方向进行搜索

2. 当一个方向搜索完了,还没有搜到和另一个方向的公共点时,就直接结束,此时一定无解了! 这是因为: 不妨假设是正向搜索完了,还没有搜到与另一个方向的公共点。注意到我们的“双向搜索”本质上和“单向搜索”的答案(有解性)应当是相同的。而如果只用“单向搜索”搜不到答案,那么双向搜索也一定搜不到答案(反证法可证),而现在“正向搜索”搜完了,这实际上相当于“单向搜索”搜索完了,也没搜到答案,因此“双向搜索”就没有必要继续进行下去了,因为一定不会有答案了。

3. 使用了string 类的substr来完成取子串,string类的+ 来实现字符串拼接, string类的=来实现字符串相等的判断

4. 使用unordered_map,不用map, 这样会更快

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<unordered_map>
#include<algorithm>
using namespace std;
const int N=7;
queue<string> qa,qb;
string a[N],b[N];
unordered_map<string,int> ma,mb;
string A,B;
int n;

int extend(string cur,int flag){
	if(flag==0){
		for(int i=0;i<cur.size();i++){
			for(int j=1;j<=n;j++){
				if(cur.substr(i,a[j].size())==a[j]){
					string tmp;
					tmp=cur.substr(0,i)+b[j]+cur.substr(i+a[j].size());
					if(mb[tmp]){
						int res=mb[tmp]-1+ma[cur];
						return res;
					}
					if(ma[tmp]) continue;
					ma[tmp]=ma[cur]+1;
					qa.push(tmp);
				}
			}
		}
	}
	else{
		for(int i=0;i<cur.size();i++){
			for(int j=1;j<=n;j++){
				if(cur.substr(i,b[j].size())==b[j]){
					string tmp;
					tmp=cur.substr(0,i)+a[j]+cur.substr(i+b[j].size());
					if(ma[tmp]){
						int res=ma[tmp]-1+mb[cur];
						return res;
					}
					if(mb[tmp]) continue;
					mb[tmp]=mb[cur]+1;
					qb.push(tmp);
				}
			}
		}
	}
	return 11;
}

int bfs(){
	qa.push(A);
	ma[A]=1;
	qb.push(B);
	mb[B]=1;
	if(A==B) return 0;
	while(qa.size()&&qb.size()){
		int t;
		if(qa.size()<=qb.size()){
			string cur=qa.front();
			qa.pop();
			t=extend(cur,0);
		}
		else{
			string cur=qb.front();
			qb.pop();
			t=extend(cur,1);
		}
		if(t<=10) return t;
	}
	return 11;
}


int main(void){
	
	cin >> A>> B;
	n=1;
	while(cin >> a[n] >> b[n]) n++;
	n--;
	int res=bfs();
	if(res>10) printf("NO ANSWER!");
	else printf("%d",res);
	
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值