题目地址:
https://www.acwing.com/problem/content/description/192/
已知有两个字串 A A A, B B B及一组字串变换的规则(至多 6 6 6个规则):
A 1 → B 1 A_1→B_1 A1→B1
A 2 → B 2 A_2→B_2 A2→B2
… … …
规则的含义为:在 A A A中的子串 A 1 A_1 A1可以变换为 B 1 B_1 B1、 A 2 A_2 A2可以变换为 B 2 … B_2… B2…。
例如:A=abcd B=xyz
变换规则为:
abc → xu, ud → y, y → yz
则此时, A A A可以经过一系列的变换变为 B B B,其变换的过程为:
"abcd → xud → xy → xyz"
共进行了三次变换,使得 A A A变换为 B B B。
输入格式:
输入格式如下:
A B A\ B A B
A 1 B 1 A_1\ B_1 A1 B1
A 2 B 2 A_2\ B_2 A2 B2
… … … … ……
第一行是两个给定的字符串 A A A和 B B B。接下来若干行,每行描述一组字串变换的规则。所有字符串长度的上限为 20 20 20。
输出格式:
若在 10 10 10步(包含 10 10 10步)以内能将 A A A变换为 B B B,则输出最少的变换步数;否则输出NO ANSWER!
。
可以用双向BFS。每次考虑出队的时候,可以将元素较少的那个队列先进行扩展。代码如下:
#include <iostream>
#include <queue>
#include <unordered_map>
using namespace std;
const int N = 6;
int n;
string a[N], b[N];
int res;
// 返回从q的队头拓展出一步是否会和和另一个方向“会师”
bool extend(queue<string>& q, unordered_map<string, int>& da, unordered_map<string, int>& db, string a[], string b[]) {
string t = q.front();
q.pop();
// 枚举从t的哪个位置开始变换
for (int i = 0; i < t.size(); i++)
// 枚举是否能变换,当能变换的时候,求一下能变为谁
for (int j = 0; j < n; j++)
if (t.substr(i, a[j].size()) == a[j]) {
string ne = t.substr(0, i) + b[j] + t.substr(i + a[j].size());
// 如果之前走到过这个状态,则略过
if (da.count(ne)) continue;
// 否则更新一下距离
da[ne] = da[t] + 1;
// 如果与另一边会师了,则记录一下答案,并返回true
if (db.count(ne)) {
res = da[ne] + db[ne];
return true;
}
q.push(ne);
}
return false;
}
int bfs(string A, string B) {
unordered_map<string, int> da, db;
da[A] = db[B] = 0;
queue<string> qa, qb;
qa.push(A), qb.push(B);
while (qa.size() && qb.size()) {
if (qa.size() <= qb.size()) {
if (extend(qa, da, db, a, b)) return res;
} else if (extend(qb, db, da, b, a)) return res;
}
return 11;
}
int main() {
string A, B;
cin >> A >> B;
while (cin >> a[n] >> b[n]) n++;
int res = bfs(A, B);
if (res > 10) cout << "NO ANSWER!" << endl;
else cout << res << endl;
return 0;
}
时间复杂度 O ( V + E ) O(V+E) O(V+E)。