https://www.acwing.com/problem/content/description/192/
已知有两个字串 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
这道题如果用简单的搜索的话,很有可能会超时,为了提高效率,用双向搜索来解决这道题,其中双向搜索最重要的一点就是,用两个队列来分别从两端进行搜索,在用两个map容器分别记录走到搜索点所用的步数,即可。
另外拓展一些关于字符串的函数,
s.substr(begin, end)
返回一个string,包含s中从begin开始到end的字符串,或s中从begin开始的的n个字符的拷贝(begin的默认值是0,n的默认值是s.size() - pos,即不加参数会默认拷贝整个s)
#include<bits/stdc++.h>
#include<queue>
#include<map>
using namespace std;
int n=0;//记录规则个数
string a[7],b[7];//分别存放变换前后的规则
string A,B;
queue<string> qa,qb; //分别从A,B开始bfs的队列
map<string,int> da,db;//分别记录到起点A,终点B的距离(搜索的步数 )
int bfs(){
if(A==B){ //这步必不可少,若没有这步就会tle, 下面没有判断当A==B时的情况
return 0;
}
qa.push(A),qb.push(B);
da[A]=0;
db[B]=0;
while(!qa.empty()&&!qb.empty()){//若双向队列有一方为空,说明有一端已经遍历完成,但仍未变成目标字符,即不可能实现
if(qa.size()<=qb.size()){// 哪个方向的队列更小,它的拓展空间更小,先拓展哪一方向
// int step=0;
string t=qa.front();
qa.pop();
if(da[t]>10||db[t]>10) { //不满足条件
return 11;
}
for(int i=0;i<t.size();i++){ // eg.当t=abcd时,从a开始判断有没有可变换的规则
for(int j=0;j<n;j++){//共n个规则,每次遍历一遍
if(t.substr(i,a[j].size())==a[j]){
string temp=t.substr(0,i)+b[j]+t.substr(i+a[j].size(),t.size());
if(db.count(temp)) return db[temp]+da[t]+1;//说明另一方向已经遍历过,两个方向碰头,返回两着距离之和加一,即为最短距离
if(da.count(temp)) continue;//已经遍历过
qa.push(temp);//没有遍历过这种情况,该方向的队列
da[temp]=da[t]+1;//temp离起点的距离为t到七点的距离加一
}
}
}
}
else{
string t=qb.front();
qb.pop();
for(int i=0;i<t.size();i++){
for(int j=0;j<n;j++){
if(t.substr(i,b[j].size())==b[j]){
string temp=t.substr(0,i)+a[j]+t.substr(i+b[j].size(),t.size());
if(da.count(temp)) return db[t]+da[temp]+1;
if(db.count(temp)) continue;
qb.push(temp);
db[temp]=db[t]+1;
}
}
}
}
}
return 11;//不可能实现的返回11,超过10,输出NO ANSWER!
}
int main(){
cin>>A>>B;
while(cin>>a[n]>>b[n]) n++; // 在不知道规则个数的情况下采用这耳中输入方式
int p=bfs(); //一般情况下,bfs都是返回void类型,但是这道题返回int类型比较方便处理结果
if(p>10) cout<<"NO ANSWER!"<<endl;
else cout<<p<<endl;
return 0;
}