AcWing 190 字串变换
双向广搜的思路:从初始状态和目标状态同时开始搜索,直到找到两个搜索的状态的交集,可以大大节省搜索需要的时间。注意每次搜索都要把一层完整的搜完,避免漏掉答案的情况出现
双向广搜示意图:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <unordered_map>
using namespace std;
const int N = 6;
int n;
string A, B;
string a[N], b[N];
//储存字符串的队列, 第一个字符串的优先队列,第二个字符串的优先队列,第一个字符串,第二个字符串
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()]; //d表示距离
while (q.size() && da[q.front()] == d) //当队列不空,且当前队列队首的距离还在这一层
{
auto t = q.front(); //取出队首元素
q.pop();
for (int i = 0; i < n; i ++ ) //这一层遍历的是n个变换规则
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()); //替换后的字符串
if (db.count(r)) return da[t] + db[r] + 1;
//如果在目标串中的能找到新生成的串,说明双端队列搜索找到了交集,可以返回了
if (da.count(r)) continue;
//如果在a中曾经有过这个状态,就不用再更新距离,也不用把这个状态加入队列中
da[r] = da[t] + 1;
q.push(r);
}
}
return 11;
}
int bfs()
{
if (A == B) return 0; //如果两个字符串相同,那就直接返回
queue<string> qa, qb; //两个队列
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);
//t返回的是两个队列广搜到交点时,两个队列总共多少步
if (t <= 10) return t; //如果步数小于10,就
if ( ++ step == 10) return -1; //如果超过了规定步数,就直接返回-1
}
return -1;
}
int main()
{
cin >> A >> B;
while (cin >> a[n] >> b[n]) n ++ ; //读入变换规则并记录规则个数
int t = bfs();
if (t == -1) puts("NO ANSWER!"); //如果返回-1,就说明没找到
else cout << t << endl;
return 0;
}