题目描述:
题目链接:
https://www.acwing.com/problem/content/192/
思路:
这个题是要求“最小步数”,比较容易想到是用BFS来进行搜索,但是直接BFS的话状态数太多了,时间复杂度会到:,其中L是字符串的长度,N是一个字符串的可能变换到的后继字符串数。肯定会超时。
因此,本题使用了“双向搜索”的技巧,i.e. 分别从起点和终点向中间进行搜索,如果搜到了“公共点”,那这个公共点就是答案。 使用双向搜索的时间复杂度是:
在具体实现的时候,使用了一些实现技巧(需要注意!):
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;
}