本题采用双向 BFS 来解决。

首先分析一下时间复杂度:变换规则数 R6R \leq 6 ,字符串长度 L20L \leq 20,操作数在 10 次以内,用朴素 BFS 的时间复杂度为 O((LR)10)O((L\cdot R)^{10}) ,完全爆炸;采用双向 BFS 的时间复杂度为 O((LR)5)O((L\cdot R)^{5}) ,相对于朴素做法降低了很多的时间复杂度。

但对于本题,可能出现以下情况:每次变换可能在字符串的多个位置替换、变换规则可能形成“无限繁殖”的状态等,导致搜索空间飞速增长。双向 BFS 并不能保证通过本题的数据,但也是一种优化搜索的方法。

双向 BFS 的具体步骤:

  • 开两个队列 qa,qbqa,qb,一开始分别把起点和终点丢入队列中。
  • 每次选择较小的队列进行扩展,将队头其可能经过变换规则得到的字符串丢入队列中(注意假如扩展 qbqb ,那么需要将规则反向)。扩展的时候注意判断:假如扩展后的字符串在本队列中,则不用入队;若在另外一个队列中,说明双向搜索会合了,直接返回距离之和即可;否则更新一下距离并入队。
  • 若一直没有会合,说明没有找到。

以扩展 qaqa 为例,设 da[i],db[i]da[i],db[i] 为状态 iiqa,qbqa,qb 中的操作数,假如发现扩展后的字符串在 qbqb 中出现过,则直接返回 da[i]+db[j]+1da[i]+db[j]+1 ,其中 jj 是由状态 ii 扩展得到的新状态。

我使用 unordered_map 处理映射关系,具体代码见下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <bits/stdc++.h>
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[],string b[])
{
int d = da[q.front()];
while(!q.empty() && da[q.front()] == d)
{
auto t = q.front();
q.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 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;
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.empty() && !qb.empty())
{
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 >> A >> B;
while(cin >> a[n] >> b[n]) n++;
int t = bfs();
if(t == -1) puts("NO ANSWER!");
else cout << t << endl;
return 0;
}