题目描述
已知有两个字串 A,B 及一组字串变换的规则(至多 6 个规则),形如:
- A1→B1。
- A2→B2。
规则的含义为:在 A 中的子串 A1 可以变换为 B1,A2 可以变换为 B2⋯。
例如:A=abcd,B=xyz,
变换规则为:
- abc→xu,ud→y,y→yz。
则此时,A 可以经过一系列的变换变为 B,其变换的过程为:
- abcd→xud→xy→xyz。
共进行了 3 次变换,使得 A 变换为 B。
输入格式
第一行有两个字符串 A,B。
接下来若干行,每行有两个字符串 Ai,Bi,表示一条变换规则。
输出格式
若在 10 步(包含 10 步)以内能将 A 变换为 B,则输出最少的变换步数;否则输出 NO ANSWER!
。
输入输出样例
输入 #1
abcd xyz abc xu ud y y yz
输出 #1
3
说明/提示
对于 100% 数据,保证所有字符串长度的上限为 20。
题解思路
这里要介绍的是迭代加深的一个搜索思路,题目要求我们找十步之内的解,找不到就返回"NO ANSWER!",也就是说我们最多搜索到第十层就可以截止了,但是这样的复杂度还是非常高的,题目中说最多6条规则,那么最坏的情况下是6的十次方量级的计算量,但是我们只需要找一种符合条件的就可以了,而且层数越少步数肯定就越少,所以我们想到,是不是可以在dfs外面套一个1-10的循环,什么意思呢,就是第一次搜索到第一层,第二次搜索到第二层...以此类推即可。这样的话大概率是要小于直接递归十层的计算量的。像这样:
while (k <= 10) {
dfs(a, 0);
mp.clear();//这里一定要注意清空mp,因为上一层的时候mp已经保存了很多字符串!!!
if (ans != INF) break;
k++;
}
接下来还是去思考怎么去剪枝,既然是求最短步长,那我们在求解的时候如果遇到比最小步长大的话就直接返回即可,具体有下面几步操作:
(1)首先我们肯定在最外面有一个全局变量ans来记录最短步长,如果在某一层我们发现step不比ans小,那么这个肯定不会是最优解,直接返回即可。
(2)我们用一个哈希表来记录每一个变化后的字符串出现时的最小步长,这样的话我们往后遍历的时候如果发现之前记录的最短步长比不比现在的step大,那么这个肯定也没有之前的策略优秀,直接返回continue即可。
题解代码
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 10, INF = 1e9;
int cnt = 0, ans = INF, k = 1;
string a, b;
string from[MAXN], to[MAXN];
map<string, int> mp; //用来存储变化到每个字符串所需要的最短步数
//开始深搜
void dfs(string curr, int step) {
mp[curr] = step;//记录步数
if (step > k) return ;//如果step大于当前迭代层,直接返回
if (curr == b) {//满足条件,改变ans的值
ans = min(ans, step);
return ;
}
//搜索剪枝,因为当前还没到b状态,那么step至少要加一,
//如果加一大于等于之前的最小步长,那么也不用继续搜索了
if (ans <= step + 1) return ;
//遍历所有的规则
for (int i = 0; i < cnt; i++) {
//尝试每一次的变换
int pos = curr.find(from[i]);
while (pos != -1) {
//这里因为第34行需要继续从curr里面找form[i],所以要用一个字符串来把curr复制一份去操作
string tmp = curr;
//这两行可以看成把tmp中的from子串替换成to
tmp.erase(pos, from[i].size());
tmp.insert(pos, to[i]);
if (mp.find(tmp) == mp.end() || mp[tmp] > step + 1) {
//如果变化后的tmp之前没有出现过,或者之前的步长大于当前步长加一 (step + 1到达下一状态)
//就继续递归
dfs(tmp, step + 1);
}
//找到下一个在curr中出现的from[i]的位置
pos = curr.find(from[i], pos + 1);
}
}
return ;
}
int main() {
cin >> a >> b;
while (cin >> from[cnt] >> to[cnt]) cnt++;
while (k <= 10) {
dfs(a, 0);
mp.clear();//这里一定要注意清空mp,因为上一层的时候mp已经保存了很多字符串!!!
if (ans != INF) break;
k++;
}
if (ans == INF) cout << "NO ANSWER!" << endl;
else cout << ans << endl;
return 0;
}