【AcWing算法提高课】2.1.3BFS中的双向广搜和A-star

一、双向广搜

在一般的BFS问题中,我们都是单向BFS,即只从起点开始扩展,某一次扩展到终点 (终点第一次出队) 就结束。当搜索范围只在某个矩阵或图的内部,点数比较少的情况下 (如“走迷宫”一题),从起点搜到终点只需线性时间复杂度 (每个点只被便遍历一次),完全可以采用单向BFS且不会超时。但是当扩展次数越多,状态空间越庞大时 (状态空间指数增加),若还是按照单向BFS,中间遍历到的不同状态会非常多,效率极其低下 (如下图灰色区域)。

在这里插入图片描述

若我们采用双向广搜,即从起点向终点扩展的同时,也从终点向起点扩展,搜到的标志是两者均扩展到某个点,在搜索过程中遍历到的状态会少很多 (如下图绿色区域)。

在这里插入图片描述

在使用双向广搜时,朴素的做法是每次从起点扩展一层,再从终点扩展一层。还有一个小优化:可以比较从起点扩展来的和从终点扩展来的状态空间,哪个小就从哪边扩展一层。

二、字串变换

190.字串变换 题目链接

在最坏情况下,有 6 6 6 种变换规则,长度为 20 20 20 的字符串的每个位置都能应用这些规则,一共要扩展 10 10 10 次,状态空间达 12 0 10 120^{10} 12010。就算一个字符串只有一个位置能应用一个规则,扩展 10 10 10 次后状态空间也达 6 10 6^{10} 610。可见如果不用双向广搜必超时。

注意在从终点扩展时,字串变换的规则是颠倒的 ( B 1 → A 1 , B 2 → A 2 , . . . B_1\to A_1, B_2\to A_2,... B1A1,B2A2,...)。在对 string t 实行一次变换时,可以采用 t.substr(i, j) 函数得到从 t[i] 开始长度为 j j j 的子串,其中 j j j 可省略,默认截到字符串末尾。

代码实现:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <unordered_map>
#include <queue>

using namespace std;

const int N = 6;

int n;
string A, B;
string a[N], b[N];

int extend(queue <string> &q, unordered_map <string, int> &da, unordered_map <string, int> &db, string a[], string b[]){
   
    int d = da[q.front()];
    while (q.size() && da[q.front()] == d){
     //注意一次是扩展一层,不是只扩展队头
        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]){
   
                    string state = t.substr(0, i) + b[j] + t.substr(i + a[j].size());
                    if (db.count(state)) return da[t] + 1 + db[state];  //记得+1
                    if (da.count(state)) continue;
                    da[state] = da[t] + 1;
                    q.push(state);
                }
    }
    
    return 11;
}

int bfs(string A, string B){
   
    if (A == B) return 0;
    queue <string> qa, qb;
    unordered_map <string, int> da, db;  //存储到起点、终点的变换次数,不用从1开始
    
    qa.push(A), qb.push(B);
    da[A] = db[B] = 0;
    
    int step = 0;
    while (qa.size() && qb.size()){
   
        
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值