NOIP2007/BZOJ1999 字串变换【题解】

描述

已知有两个字串 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值