AcWing 字串变换
已知有两个字串 AA, BB 及一组字串变换的规则(至多 66 个规则):
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
共进行了三次变换,使得 A 变换为 B。
输入格式
输入格式如下:
A B
A1 B1
A2 B2
… …
第一行是两个给定的字符串 A 和 B。
接下来若干行,每行描述一组字串变换的规则。
所有字符串长度的上限为 20。
输出格式
若在 10 步(包含 10 步)以内能将 AA变换为 B ,则输出最少的变换步数;否则输出 NO ANSWER!
。
输入样例:
abcd xyz
abc xu
ud y
y yz
输出样例:
3
难度:中等 |
时/空限制:1s / 64MB |
总通过数:5384 |
总尝试数:11303 |
来源:《算法竞赛进阶指南》 |
算法标签 |
#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.size() &&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.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 >> A >> B;
while(cin >> a[n] >> b[n])n ++;
int t = bfs();
if (t == -1) puts("NO ANSWER!");
else cout << t << endl;
return 0;
}
假设每次决策数量是 K,那么如果直接BFS,最坏情况下的搜索空间是 Ke10,非常大,所以会TLE或者MLE。
如果采用双向BFS,则可以把搜索空间降到 2Ke5。