已知有两个字串 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’
共进行了三次变换,使得 A 变换为B。
输入格式
输入格式如下:
A B
A1 B1
A2 B2 |-> 变换规则
… … /
所有字符串长度的上限为 20。
输出格式
若在 10 步(包含 10步)以内能将 A 变换为 B ,则输出最少的变换步数;否则输出”NO ANSWER!”
输入样例:
abcd xyz
abc xu
ud y
y yz
输出样例:
3
解析:
双向广搜一般应用于最小步数模型
每次选择队列中数量较小的一部分,以达到平衡
#include<bits/stdc++.h>
using namespace std;
const int N=1e5;
int n;
int step;
string a[N],b[N];
int extend(queue<string> &q,unordered_map<string,int> &da,unordered_map<string,int> &db,string a[],string b[])
{
auto t=q.front();
q.pop();
for(int i=0;i<t.size();i++)//枚举t串的每个部分,看是否能被替换
{
for(int j=0;j<n;j++)//枚举替换的部分
{
if(t.substr(i,a[j].size())!=a[j]) continue;
string r=t.substr(0,i)+b[j]+t.substr(a[j].size()+i);//找到了可以替换的地方,那么我们需要截取t串下标为0,长度i+替换的部分+后面剩余的部分
if(db.count(r)!=0) return db[r]+1+da[t];//如果r这个状态存在的话db中,那么我们就需要返回db[r]+1+da[t](解释:db[r]搜到r的步数。da[t]+1:t这个状态转换为r这个状态所以需要+1)
if(da.count(r)) continue;//如果r状态再da出现过就不需要了
da[r]=da[t]+1;//距离+1
q.push(r);//入队
}
}
return -1;
}
int bfs(string A,string B)
{
unordered_map<string,int> da,db; //设置双向
queue<string> qa,qb; //初始化
qa.push(A);
da[A]=0;
qb.push(B);
db[B]=0;
while(qa.size()&&qb.size())
{
int t;
if(da[qa.front()]+db[qb.front()]>10) return 11;//如果两个方向所搜到的状态,都无法在图上连通,且两个方向上的步数和大于10,则不满足。
if(qa.size()<=qb.size()) t=extend(qa,da,db,a,b);//因为是双向,每次去队列数量较小的一面,以致达到平衡
else t=extend(qb,db,da,b,a);
if(t!=-1) return t;
}
return 11;
}
int main()
{
string A,B;
cin>>A>>B;
while(cin>>a[n]>>b[n]) n++;
step=bfs(A,B);
if(step>10) puts("NO ANSWER!");
else printf("%d\n",step);
}