这题使用双向bfs.首先已经知道初始的字符串和终点字符串,我们不妨以初始字符串和终点字符串分别为起点进行bfs,当两个互相搜索到的时候判断两个字符串状态的"距离"(操作次数)是否小于等于10次.
代码如下:
#include <bits/stdc++.h>
// #define LOCAL
#define INF 0x3f3f3f3f3f3f3f3f
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define int long long
#define debug(a) cout << #a << "=" << a << endl;
using namespace std;
const int N = 8;
string bg, ed;
unordered_map<string, int> dbg, ded;
queue<string> qbg, qed;
bool ok;
string a[N], b[N];
int cnt;
int bfs(queue<string> &q, unordered_map<string, int> &da, unordered_map<string, int> &db, string aa[], string bb[]){
int sz = q.size();
for (int i = 0; i < sz; ++i){
string now = q.front();
q.pop();
for (int i = 0; i < cnt; i++){
for (int j = 0; j < now.size(); j++)
if (now.substr(j, aa[i].size()) == aa[i]){
string r = now.substr(0, j) + bb[i] + now.substr(j + aa[i].size());
if (db.count(r))
return da[now] + db[r] + 1;
if (da.count(r))
continue;
da[r] = da[now] + 1;
q.push(r);
}
}
}
return 11;
}
signed main()
{
#ifdef LOCAL
freopen("input.in", "r", stdin);
freopen("output.out", "w", stdout);
#endif
IOS;
cin >> bg >> ed;
dbg[bg] = ded[ed] = 0;
qbg.emplace(bg);
qed.emplace(ed);
string ta, tb;
while (cin >> ta >> tb)
{
a[cnt] = ta, b[cnt] = tb;
++cnt;
}
if (bg == ed)
{
cout << 0 << '\n';
return 0;
}
bool has_ans = false;
int step = 0;
int t;
while (qbg.size() && qed.size())
{
if (qbg.size() < qed.size())
t = bfs(qbg, dbg, ded, a, b);
else
t = bfs(qed, ded, dbg, b, a);
if (t <= 10){
has_ans = true;
break;
}
if (++step == 10)
break;
}
if (has_ans)
cout << t << "\n";
else cout << "NO ANSWER!\n";
}
通过这道题,我总结出几个双向bfs的要注意的点:
1.进行双向bfs时,每一步的拓展其实是拓展一层的节点,我们假设用队列进行bfs,当前一轮拓展初始时队列的大小为size,那么我们就要拓展size次才能把一层的节点全部拓展更新.至于我们为什么要拓展更新一层的节点,这是因为要维持队列的分层性和单调性.
2.计算步数时,不能直接用拓展的次数表示步数(不是不能,而是这样做实在不方便),而是用d数组维护步数,每次更新时更新d数组
3.双向bfs一定要考虑反向的问题,从终点出发的路径和起点的路径是互为镜像的
4.不用同时开d数组和vis数组维护每个点的访问情况,巧用count函数即可.
还有一点就是,substr()函数是真的方便.
substr(pos, length)表示从pos位置起长度为length的子串
substr(pos)表示从pos位置起到字符串结尾的子串