190. 字串变换 - AcWing题库 已知有两个字串 , 及一组字串变换的规则(至多 个规则):
规则的含义为:在 中的子串 可以变换为 、 可以变换为 。
例如:=abcd
=xyz
变换规则为:
abc
xu
ud
y
y
yz
则此时, 可以经过一系列的变换变为 ,其变换的过程为:
abcd
xud
xy
xyz
共进行了三次变换,使得 变换为 。
输入格式
输入格式如下:
A B
A1 B1
A2 B2
… …
第一行是两个给定的字符串 和 。
接下来若干行,每行描述一组字串变换的规则。
所有字符串长度的上限为 。
输出格式
若在 步(包含 步)以内能将 变换为 ,则输出最少的变换步数;否则输出 NO ANSWER!
。
输入样例:
abcd xyz
abc xu
ud y
y yz
输出样例:
3
思路
给一个串A, 和6个变换规则, 求变化到B串要用的最少步数。
应用一个规则时, 需要枚举串A, 找到可以应用的点, 然后应用。因此, 最坏情况下, 长度为20的A串, 每个位置都能应用6个规则, 搜10次的话为 。不过肯定不会都能应用, 可以只考虑规则数, 时间复杂度为 , 这同样还是太高了。
需要进行优化, 这里采用双向BFS的方法, 将时间复杂度优化到 , 很舒服。 双向BFS过程为: 定义俩队列qa qb, qa为从起点开始的BFS, qb为从终点开始的BFS。 每次扩展时优先扩展队列长度小的。
该题扩展时需要枚举当前字符串所有位置, 然后再枚举规则进行应用, 若新得出的串未被该方向上的哈希表标记, 则加入队列并标记。 若已被另一个方向的哈希表标记, 说明搜索相遇, 当前路径就是最小路径, 输出答案即可。 若扩展时有一个队列为空, 说明起点和中间不存在通路, 直接返回无解。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
#include <queue>
#include <unordered_map>
using namespace std;
const int N = 6;
int n;
string a[N], b[N];
int temp;
int extend(queue<string> &q, unordered_map<string, int> &da, unordered_map<string,int> &db, string a[], string b[])
{
string t = q.front();
q.pop();
for(int i = 0; i < t.size(); i++)
for(int j = 0; j < 6; j++)
{
if(t.substr(i, a[j].size()) == a[j])
{
string state = t.substr(0,i) + b[j] + t.substr(i + a[j].size());
if(db.count(state)) return da[t] + db[state] + 1;
if(da.count(state)) continue;
q.push(state);
da[state] = da[t] + 1;
}
}
return 11;
}
int bfs(string A, string B)
{
if(A == B) return 0;
queue<string> qa,qb;
unordered_map<string, int> da, db;
qa.push(A), da[A] = 0;
qb.push(B), db[B] = 0;
while(qa.size() && qb.size())
{
int t = 11;
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;
}
return 11;
}
int main()
{
string A, B;
cin >> A >> B;
while(cin >> a[n] >> b[n])n++;
int step = bfs(A,B);
if(step > 10) puts("NO ANSWER!");
else cout << step << endl;
return 0;
}