AcWing 190 字串变换

题目描述:

已知有两个字串 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

 分析:

本题如果采用普通的BFS求状态转换的最小步数,字符串最长长度是20,假设每一个字符上最多只能进行一种变换,十步以内最多可以进行20^10也就是1024*10^10种状态的搜索,显然这么大的数字会超时或者超空间。即使每个字符串只会做一次变换,也一共要做6^10也就是六千多万种状态的搜索。因为BFS搜索过程中状态的数量是指数级递增的,所以时间增长的特别快。假设从A到B状态一共要做十次转化,C是A到B的一个中间状态,如果A到C要做5次转化,则C到B还要做5次转化。我们同时从A和B进行BFS,各进行五次搜索都搜到C的时候停止,则在两种情况下涉及的状态分别是2 * 20^5=640w以及2 * 6^5 约等于1.54w个状态,显然,这两个状态数都要远小于之前的状态数,或者说只有之前状态数开根号后这么多状态。因此,本题分别从起点和终点同时进行BFS的这种双向BFS的方法能够很好的解决问题。

需要解决的就是如何同时进行BFS搜索,可以先将开始状态和终点状态分别加入两个不同的队列,如何当两个队列都非空的时候,先对较小的那个队列出队做BFS,以减小状态的增加数量,并且如果两个队头元素的步数之和超过了10,就可以剪枝直接返回了。状态的转换需要考虑从状态x能够转换到哪些状态,因为字符串较短这里直接暴力搜索就可以解决了,具体实现见代码:

#include <iostream>
#include <queue>
#include <algorithm>
#include <string>
#include <unordered_map>
using namespace std;
const int N = 6;
string a[N],b[N];
int n;
unordered_map<string,int> d1,d2;
queue<string> q1,q2;
int extend(queue<string> &q,unordered_map<string,int> &d,string *a,string * b){
    string t = q.front();
    q.pop();
    for(int i = 0;i < t.size();i++){
        for(int j = 0;j < n;j++){
            if(t.substr(i,a[j].size()) != a[j]) continue;
            string u = t.substr(0,i) + b[j] + t.substr(i + a[j].size());
            if(d.count(u))  continue;
            d[u] = d[t] + 1;
            if(d1.count(u) && d2.count(u))  return d1[u] + d2[u];
            q.push(u);
        }
    }
    return -1;
}
int bfs(string x,string y){
    q1.push(x),q2.push(y);
    d1[x] = 0,d2[y] = 0;
    while(q1.size() && q2.size()){
        if(d1[q1.front()] + d2[q2.front()] > 11)    return 11;
        int t;
        if(q1.size() <= q2.size())  t = extend(q1,d1,a,b);
        else    t = extend(q2,d2,b,a);
        if(t != -1) return t;
    }
    return 11;
}
int main(){
    string A,B;
    cin>>A>>B;
    while(cin>>a[n]>>b[n])  n++;
    int res = bfs(A,B);
    if(res > 10)    puts("NO ANSWER!");
    else    cout<<res<<endl;
    return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值