已知有两个字串 AA, BB 及一组字串变换的规则(至多 66 个规则):
A1→B1A1→B1
A2→B2A2→B2
…
规则的含义为:在 AA 中的子串 A1A1 可以变换为 B1B1、A2A2 可以变换为 B2…B2…。
例如:AA=
abcd
BB=xyz
变换规则为:
abc
→→xu
ud
→→y
y
→→yz
则此时,AA 可以经过一系列的变换变为 BB,其变换的过程为:
abcd
→→xud
→→xy
→→xyz
共进行了三次变换,使得 AA 变换为 BB。
输入格式
输入格式如下:
AA BB
A1A1 B1B1
A2A2 B2B2
… …第一行是两个给定的字符串 AA 和 BB。
接下来若干行,每行描述一组字串变换的规则。
所有字符串长度的上限为 2020。
输出格式
若在 1010 步(包含 1010 步)以内能将 AA 变换为 BB ,则输出最少的变换步数;否则输出
NO ANSWER!
。输入样例:
abcd xyz abc xu ud y y yz
输出样例:
3
此题用单向搜索会tle所以需要双向广搜,为何双向广搜时间复杂度会减低?
{
若用单向广搜,假设每次扩展a步,共需扩展b层,则复杂度为a的10次方
若用双向广搜,则工序扩展(a的b / 2次方) / 2;
所以单向广搜是双向广搜的(a的b / 2)/ 2倍(用单向时间复杂度除以双向时间复杂度)
也可以画图理解:
如图:
红色区域表示单搜,黑色表示双搜
}
从终点点往起点,和从起点往终点同时搜,每次搜一层,为什么不是一个点一个点搜?
而是一层一层搜? 如图:
如上图,如果每次不是扩展完整一层,而是只扩展一个点。此时上面该扩展点 aa 了,点 aa 搜到了下半部分的点 cc,此时算出的最短路长度是 x+1+y+1+1=x+y+3x+1+y+1+1=x+y+3。但是最优解可能是后面还没扩展到的点 bb 和点 dd 之间的路径,这条路径的长度是 x+1+y+1=x+y+2x+1+y+1=x+y+2。
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
const int N = 6;
int n;
string a[N], b[N];
string start, last;
int extend(queue<string> &qa, unordered_map<string, int> &da, unordered_map<string, int> &db, string a[N], string b[N])
{
int d = da[qa.front()];//一层一层搜
while (qa.size() && da[qa.front()] == d)
{
auto t = qa.front();
qa.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 res;
res += t.substr(0, j) + b[i] + t.substr(j + a[i].size());
if (db.count(res)) return da[t] + db[res] + 1;//若在另一个队列中出现过,说明找到了
if (da.count(res)) continue;//否则在当前队列出现过,不必重复入队
da[res] = da[t] + 1;
qa.push(res);
}
}
}
return 11;
}
int bfs(string st, string ed)
{
if (st == ed) return 0;
unordered_map<string, int> da, db;
queue<string> qa, qb;
da[st] = 0, db[ed] = 0;
qa.push(st), qb.push(ed);
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 >> start >> last;
while (cin >> a[n] >> b[n]) n ++ ;
int t = bfs(start, last);
if (t == -1) puts("NO ANSWER!");
else printf("%d\n", t);
return 0;
}