(算法提高课)搜索-双向广搜

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=aa, B=bb
变换规则为:
a → b
此时,不能将两个 a 在一步中全部转换为 b,而应当分两步完成。
输入格式
输入格式如下:
A B
A1 B1
A2 B2
… …
第一行是两个给定的字符串 A和 B。
接下来若干行,每行描述一组字串变换的规则。
所有字符串长度的上限为 20。
输出格式
若在 10 步(包含 10 步)以内能将 A 变换为 B ,则输出最少的变换步数;否则输出 NO ANSWER!。
输入样例:
abcd xyz
abc xu
ud y
y yz
输出样例:
3
难度:中等

按照题目条件,本题最多需要搜索十层;设每层根据规则展开 K K K个字符串,则最坏搜索情况下时间复杂度达到 K 10 K^{10} K10,很容易超时;
此时结合题设条件可以采用双向广搜,A向B搜索的同时,B也向A一层一层的展开搜索(本题中A可以根据变化到B其实B也可以按照反方向向A变化);当双向搜索的时候,搜索宽度会变窄,对时间的优化度明显有提升
注意每次搜索都必须将一层搜索完,此时统计的步数才是正确的

#include<bits/stdc++.h>
using namespace std;
const int N = 10;
string A, B;
string a[N], b[N];
int n = 0;

//A->B 或者 B->A的拓展规则相同,这里抽象出这个拓展函数
//参数分别是拓展字符队列,da是到队首的距离,db是到队尾的距离,a->b是一个匹配规则
int extend(queue<string>& q, unordered_map<string,int>& da, unordered_map<string,int>& db, string a[N], string b[N])
{
    //将在同一层的点全部搜索完,注意一定要保证同层搜索完毕,这样才能保证步数是准确的
    int dis = da[q.front()];  //记录当前的层数
    while(q.size() && da[q.front()] == dis){
        string now = q.front(); q.pop();

        for(int i = 0;i < n;i ++){
            for(int j = 0;j < now.size();j ++){
                //对于每一个规则,看当前字符串中有没有符合转化规则的字符字串
                if(now.substr(j, a[i].size()) == a[i]){
                    //对字串进行替换:目标字符之前的+替换字符+替换子串之后的
                    string state = now.substr(0, j) + b[i] + now.substr(j + a[i].size());

                    //如果从相反方向已经遍历到这个字符,注意因为是广搜,某个字符第一次被找到就是最短路径
                    if(db.count(state)) return da[now] + db[state] + 1;
                    if(da.count(state)) continue;  //如果已经被从起点已经被搜索过的话,直接跳过(当前不是距离起点的最短距离)
                    da[state] = da[now] + 1;
                    q.push(state);
                }
            }
        }
    }
    return 11; //返回一个比10大的数字,表示此轮搜索没有找到交叉字符
}

int bfs()
{
    if(A == B) return 0;   //将情况考虑全面,首先考虑二者相等
    queue<string> qa, qb;
    unordered_map<string,int> da, db;   //用于存储某个字符串到起点的距离及到终点的距离

    //初始化
    qa.push(A); qb.push(B);
    da[A] = 0; db[B] = 0;

    int step = 0;
    while(qa.size() && qb.size()){
        int t;
        //从拓展层数较少的一段开始
        if(qa.size() < qb.size())  t = extend(qa, da, db, a, b);
        else t = extend(qb, db, da, b, a);
        if(t <= 10)  return t; //搜索到了交叉字符
        step ++;
            if(step == 10)  //搜索了十层都没有找到交叉字符,不符合题意,直接返回
                return -1;
    }
    return -1;
}
int main()
{
    cin >> A >> B;
    while(cin>>a[n]>>b[n]) n ++;

    int cnt = bfs();
    if(cnt == -1) puts("NO ANSWER!");
    else cout<<cnt<<endl;
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值