描述
已知有两个字串 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
题目大意
已知有两个字串s1,s2及一些字串变换的规则a[i]变换为b[i]。输出s1变换为s2最少的变换步数。(要求a[i]是原串的子串
双向BFS
从起始状态、目标状态分别开始,两边轮流进行,每次各扩展一整层。当两边各自有一个状态在记录数组中发生重复时,就说明这两个搜索过程相遇了,可以合并得出起点到终点的最少步数。
思路
使用双向BFS算法。
建立两个队列q1,q2,分别从初始串s1、目标串s2开始进行BFS,两边轮流进行。
在每一轮中,初始串这边找到子串a[i],替换成b[i],目标串这边找到子串b[i],替换成a[i],使用map数组ans1,ans2标记每次变换后的新串x。
在BFS的过程中,第一次出现某个串x既能从初始串s1变换过来,也能从目标串s2变换时,当前轮数就是最少变换步数。
代码
#include<bits/stdc++.h>
using namespace std;
string s1,s2,a[10],b[10],q1[1010],q2[1010];
int n=1,la[10],lb[10],head1=1,tail1,head2=1,tail2,ans;
map<string,int> ans1,ans2;
//记录最少变换步数
void init()
{
cin>>s1>>s2;//初始串和目标串
while (cin>>a[n])//字串变换的规则
{
cin>>b[n];
la[n]=a[n].size();
lb[n]=b[n].size();
n++;
}
n--;
}
void bfs()
{
q1[++tail1]=s1;//起始状态
q2[++tail2]=s2;//目标状态
ans1[s1]=1;
ans2[s2]=1;
while (head1<=tail1&&head2<=tail2)
{
string k1=q1[head1++],k2=q2[head2++];//当前队头
if (ans1[k1]+ans2[k2]>10) return;//特判无解
for (int i=1;i<=n;++i)//枚举所有字串变换的规则
{
int now=k1.find(a[i],0);//查找a[i]
while (now>=0)//如果能找到a[i]
{
string x=k1;
x.replace(now,la[i],b[i]);//替换成b[i]
if (!ans1[x])
{
ans1[x]=ans1[k1]+1;
q1[++tail1]=x;//进队
}
if (ans2[x])//两个搜索过程相遇
{
ans=ans1[x]+ans2[x]-2;
printf("%d\n",ans);
exit(0);
}
now=k1.find(a[i],now+1);//查找所有a[i]
}
}//同理
for (int i=1;i<=n;++i)
{
int now=k2.find(b[i],0);
while (now>=0)
{
string x=k2;
x.replace(now,lb[i],a[i]);
if (!ans2[x])
{
ans2[x]=ans2[k2]+1;
q2[++tail2]=x;
}
if (ans1[x])
{
ans=ans1[x]+ans2[x]-2;
printf("%d\n",ans);
exit(0);
}
now=k2.find(b[i],now+1);
}
}
}
}
int main()
{
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
init();
bfs();
printf("NO ANSWER!\n");
fclose(stdin);
fclose(stdout);
return 0;
}