题目描述:
大魔术师PIPI有N个转换魔咒,每个转换魔咒可以将一个字符串变成另一个字符串。
比如说:
“PIPI”->“POPO”
“boy”->“girl”
“boy”->“u”
“isau”->“OJ”
那么对于字符串"PIPIisaboy",大魔术师PIPI可以通过2次魔咒将"PIPIisaboy"变成"POPOisagirl"。
也可以通过2次魔咒将"PIPIisaboy"变成"PIPIOJ"。
现在你知道了PIPI的所有魔咒,想让他把字符串A变成字符串B,请输出变换所需的最少步数。
输入:
输入包含单组测试样例。
第一行输入字符串A和字符串B。1≤|A|,|B|≤30。
接下来输入一个数字N,代表转换魔咒的个数(1≤N≤10)。
接下来N行,每一行输入一个转换规则 X Y,代表可以将字符串X转化为Y。 1≤|X|,|Y|≤30。
本题给出的所有字符串均不包含空格。
输出:
如果在10次之内能将A变为B,输出从字符串A变为字符串B的最少次数。否则输出-1。
样例输入:
PIPIisaboy POPOisagirl
4
PIPI POPO
boy girl
boy u
isau OJ
样例输出:
2
题解代码如下:
#include <bits/stdc++.h>
using namespace std;
string s, t;
map<string, bool> st; // 标记数组,标记字符串有没有被访问
map<string, vector<string>> trans; // key表示原字符串,value表示用来替换的字符串
struct Node{
string s; // 表示当前字符串
int t; // 表示替换为当前字符串所需要的步数
};
int bfs() {
queue<Node> q;
q.push({s, 0});
st[s] = true;
while (q.size()) {
auto now = q.front(); q.pop();
if (now.t > 10) continue; // 如果替换次数大于10则跳过
if (now.s == t) {
return now.t;
}
for (int L = 0; L < now.s.size(); L++) { // 枚举字符串左端点
for (int len = 1; L + len - 1 < now.s.size(); len++) { // 枚举字符串长度
string subs = now.s.substr(L, len); // 截取以左端点L为起点,长度为len的字符串
if (trans.count(subs)) { // 如果trans中存在有可替换的字符串则进行下一步
for (int i = 0; i < trans[subs].size(); i++) { // 遍历可替换的字符串列表
string ns = now.s;
ns.replace(L, len, trans[subs][i]); // 将源字符串的字串替换掉
if (!st[ns]) { // 如果替换后的字符串没有被访问
st[ns] = true; // 访问这个字符串
q.push({ns, now.t + 1}); // 将字符串入队
}
}
}
}
}
}
return -1;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> s >> t;
int n;
cin >> n;
while (n--) {
string a, b;
cin >> a >> b;
trans[a].push_back(b); // 将可替换a的所有字符串b存下来
}
cout << bfs() << endl;
return 0;
}