原题链接:https://www.acwing.com/problem/content/192/
虽然至多只有6个规则,但是可能有很多种变换.
比如abcabcabc, 你可以选择边第一个abc,也可以选择变第二个…
我们可以让起点A和终点B 进行双向奔赴. 假设各走五步.
搜索量大大减小.
双向BFS搜的时候, 无论是A还是B,每次都要把一层扩展出来,不能每次只扩展一个点,否则结果有可能是错的.
如果每次只扩展一个点,有可能存在以下情况,他们在某个点相遇了:然后搜索结束,然后我们就认为这是最短距离. 但是
有可能从距离相同的这一层, 到达下边还有另一个更短的路径. 但是我们没有搜到.
搜索的时候, 宽度和深度比较大的时候,一般可以考虑双向.
#include<iostream>
#include<cstring>
#include<algorithm>
#include<unordered_map>//比map稍微快一点
#include<queue>
using namespace std;
const int N=6;
int n;
string a[N], b[N];//每次操作的对象都是字符串
string A, B;
// 对q队列进行扩展, a->b
int extend(queue<string>& q, unordered_map<string, int>&da, unordered_map<string, int>& db,
string a[N], string b[N])
{
int d = da[q.front()];
// 在队列前部并且 dist等于队首元素的
// 他们都是在同一层的,都需要扩展
while (q.size() && da[q.front()] == d)
{
auto t = q.front();
q.pop();
// 枚举扩展方式
for (int i = 0; i < n; i ++ )
// 枚举每种状态可能被扩展的字符
for (int j = 0; j < t.size(); j ++ )
if (t.substr(j, a[i].size()) == a[i])
{
// 扩展后的字符
string r = t.substr(0, j) + b[i] + t.substr(j + a[i].size());
// 如果db中有了, 就是找到答案了
if (db.count(r)) return da[t] + db[r] + 1;
// da中有了, 跳过, 开启下一轮.
if (da.count(r)) continue;
da[r] = da[t] + 1;
q.push(r);
}
}
// 搜不到返回正无穷
return 11;
}
int bfs()
{
if (A == B) return 0;
// 分别存A 和 B 搜索状态的队列
queue<string> qa, qb;
// 分别对A和B, 判断搜到这个状态走了多少步数
unordered_map<string, int> da, db;
qa.push(A), qb.push(B);
da[A] = db[B] = 0;
int step = 0;
// 都不空才搜
// 因为如果其中一个队列空了,说明已经变不成该队列中没出现过的字符串了。
// 这时如果两个队列仍没出现交集,另一个队列继续搜完全没有意义。
while (qa.size() && qb.size())
{
int t;
if (qa.size() < qb.size()) t = extend(qa, da, db, a, b);
else t = extend(qb, db, da, b, a);
if (t <= 10) return t;
if ( ++ step == 10) return -1;
}
return -1;
}
int main()
{
cin >> A >> B;
while (cin >> a[n] >> b[n]) n ++ ;
int t = bfs();
if (t == -1) puts("NO ANSWER!");
else cout << t << endl;
return 0;
}