【刷题】搜索——BFS:字串变换【双向广搜模板】

news2024/10/7 8:29:39

双向广搜是BFS的一种优化方式,就是起点和终点同时往中间搜索。

假设每搜一步,都会有6种新的状态进入队列,搜索10步才能得到答案,总状态数是 1 + 6 + 6 2 + 6 3 + . . . + 6 9 1+6+6^2+6^3+...+6^9 1+6+62+63+...+69
但是假如已知终点的状态,起点终点同时搜索,则只要各搜索5步,总状态数是 2 ∗ ( 1 + 6 + 6 2 + 6 3 + 6 4 ) 2*(1+6+6^2+6^3+6^4) 2(1+6+62+63+64)

具体实现时,可以开两个队列,分别存起点和终点搜索的状态,挑两者状态数更少的那个队列进行更新。

题目

在这里插入图片描述

代码

要对字符串操作,记录该字符串是否已经在队列中和其变换距离,可用map存储。
由于不想用string,用char*以获得更好的性能,结果踩了好几个坑

  1. strncpy在拷贝时,如果源数组长度>=输入的长度,则不会以'\0'结尾,需要多拷1位
  2. queue的push虽然是值拷贝,但是传入char*时只会对指针拷贝,指针指向的内存在退出作用域后就会消失,所以newStr不能用数组形式分配在栈上,而要用new的形式分配在堆上
  3. char*作为map的key时,比较的是指针的值,也就是地址。所以即便两个字符串相同,但是在不同内存时也无法比较,要重写map的hash和比较规则。
  4. 最后为了防止内存泄漏,要手动delete自己new的内存。通过遍历map的方式来delete,因为最先输入的起点A和终点B也会被delete,所以A和B也是通过new分配内存。
  5. 注意二维数组的实际含义。
    strA的类型是指向包含M个char元素的数组指针,这样的数组指针有N个。表达式*(strA + i)(等价于strA[i])表示找到N个里的第i个数组指针,取出这个指针指向的值
    数组指针指向的是数组,数组名本质是一个常量指针,因此strA[i]的类型是指向char型的指针
    由于传的参数是strA,由上面的分析可以得到传参类型:指向包含M个char元素的数组指针。
    数组指针的写法为:char (*strA)[M];在参数中也可写作char strA[][M]。注意M不可省略。
#include <iostream>
#include <cstring>
#include <queue>
#include <unordered_map>
using namespace std;
const int N = 10, M = 25;
char strA[N][M], strB[N][M];
int n;
struct ptrCmp
{
    bool operator()(const char * s1, const char * s2) const {
        return strcmp(s1, s2) == 0;
    }
};
struct ptrHash
{
    size_t operator()(const char *str) const {
        size_t strLength = strlen(str);
        size_t hash = std::_Hash_impl::hash(str, strLength);
        return hash;
    }
};

int add(queue<char *>& qA, unordered_map<char *, int, ptrHash, ptrCmp>& disA, unordered_map<char *, int, ptrHash, ptrCmp>& disB,
        char (*strA)[M], char (*strB)[M]) { // 数组指针strA,strA是指向 包含M个char元素的数组的指针


    char* str = qA.front();
    qA.pop();

    for (int i = 0; i < strlen(str); i++ ) {
        for (int j = 0; j < n; j ++ ) {
            if (!strncmp(str + i, strA[j], strlen(strA[j]))) {
//                char newStr[M]; // 局部变量
                char* newStr = new char[M];
                strncpy(newStr, str, i);
                strncpy(newStr + i, strB[j], strlen(strB[j]));
                // +1把str最后的'\0'也拷贝过去
                strncpy(newStr + i + strlen(strB[j]), str + i + strlen(strA[j]),strlen(str) - i - strlen(strA[j]) + 1 );

                if (disB.count(newStr)) {
                    return disA[str] + disB[newStr] + 1;
                }
                if (strlen(newStr) > 20 || disA.count(newStr)) {
                    continue;
                }

                qA.push(newStr);    // push的拷贝构造函数只拷贝了newStr的值,也就是指向字符串第一个位置的指针(浅拷贝)。newStr是局部变量时,随着退出函数就会消失,队列里字符串是错的。所以要用new分配在堆里。

                disA[newStr] = disA[str] + 1;
            }
        }
    }
    return -1;
}

int bfs(char *A, char *B) {
    if (strcmp(A, B) == 0)  return 0;
    queue<char *> qA, qB;
    unordered_map<char *, int, ptrHash, ptrCmp> disA, disB;

    qA.push(A); qB.push(B);
    disA[A] = 0, disB[B] = 0;
    int ans = 0;
    while(!qA.empty() && !qB.empty()) {
        if (qA.size() < qB.size()) {
            ans = add(qA, disA, disB, strA, strB);
        }
        else {
            ans = add(qB, disB, disA, strB, strA);
        }
        if (ans > 0 && ans <= 10) return ans;
    }

    // 释放堆上的内存
    for (auto & it : disA) {
        delete it.first;
    }
    disA.clear();
    for (auto & it : disB) {
        delete it.first;
    }
    disB.clear();

    return ans;
}

int main() {
    char *A, *B;
    A = new char[M];    B = new char[M];
    cin >> A >> B;
    while(cin >> strA[n] >> strB[n]) n ++ ;

    int ans = bfs(A, B);
    if (ans < 0) cout << "NO ANSWER!";
    else cout << ans << endl;
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/443303.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Crash分析gpu非法访问地址问题

Crash分析gpu非法访问地址问题 1. 问题描述 在我司产品monkey老化过程中&#xff0c;极低概率出现gpu驱动访问非法地址导致kernel panic问题&#xff0c;在kernel panic后&#xff0c;主动触发ramdump机制&#xff0c;抓到相关的ramdump文件&#xff0c;利用crash工具进行离线…

ggrcs包2.9版本发布----增加了绘制单独rcs曲线(限制立方样条)的singlercs函数

目前本人写的ggrcs包新的2.8版本已经在CRAN上线&#xff0c;目前支持逻辑回归&#xff08;logistic回归&#xff09;、cox回归和多元线性回归。增加了绘制单独rcs曲线&#xff08;限制立方样条&#xff09;的singlercs函数。 需要的可以使用代码安装 install.packages("…

UE4/5多人游戏详解(五、创建多人游戏插件)

目录 创建插件&#xff1a; 功能制作&#xff1a; 基础构造&#xff1a; 代码&#xff1a; 准备&#xff1a; 代码&#xff1a; 之前4个内容&#xff0c;我简单的讲解了一个项目中如何加入多人会话。 现在我们做一个插件&#xff0c;这样就不需要每一次创建项目的时候就…

CT前瞻(二):Vant4实战之Card卡片与Cell单元格

文章目录 &#x1f4cb;前言&#x1f3af;关于 Card卡片 和 Cell单元格 组件&#x1f9e9;Cell单元格&#x1f9e9;Card卡片 &#x1f3af;实战代码&#x1f4dd;最后 &#x1f4cb;前言 最近在项目开发和学习的过程中&#xff0c;涉及到了Vant UI&#xff08;简称Vant&#x…

气传导耳机和骨传导耳机的区别是啥?气传导耳机有哪些优缺点?

本文主要讲解一下气传导耳机和骨传导耳机的区别、气传导耳机的优缺点&#xff0c;并推荐一些目前主流的气传导耳机款式&#xff0c;大家可以根据自身需求&#xff0c;选择自己感兴趣的部分观看。 气传导耳机和骨传导耳机不同点&#xff1a; 气传导耳机和骨传导耳机最大且最根…

HTML+CSS+JS 学习笔记(二)———CSS

&#x1f331;博客主页&#xff1a;大寄一场. &#x1f331;系列专栏&#xff1a;前端 &#x1f331;往期回顾&#xff1a;HTMLCSSJS 学习笔记&#xff08;一&#xff09;———HTML(上) HTMLCSSJS 学习笔记&#xff08;一&#xff09;———HTML(中) HTMLCSSJS 学习笔记&#…

数字信号预处理——平滑和去噪

数字信号预处理 对信号进行去噪、平滑和去趋势处理&#xff0c;为进一步分析做好准备。从数据中去除噪声、离群值和乱真内容。增强信号以对其可视化并发现模式。更改信号的采样率&#xff0c;或者使不规则采样信号或带缺失数据信号的采样率趋于恒定。为仿真和算法测试生成脉冲…

看完这篇文章你就彻底懂啦{保姆级讲解}-----(LeetCode刷题59螺旋矩阵II) 2023.4.20

目录 前言算法题&#xff08;LeetCode刷题59螺旋矩阵II&#xff09;—&#xff08;保姆级别讲解&#xff09;分析题目&#xff1a;算法思想&#xff08;重要&#xff09;螺旋矩阵II代码&#xff1a; 结束语 前言 本文章一部分内容参考于《代码随想录》----如有侵权请联系作者删…

英码科技深元ai工作站在化工园区应用,保障安全生产

当今&#xff0c;随着工业化进程的不断推进&#xff0c;化工产业作为重要的基础产业之一&#xff0c;为社会经济发展做出了巨大贡献。然而&#xff0c;随着化工园区规模的不断扩大&#xff0c;化工园区内的安全问题和环境问题也日益突出。因此&#xff0c;如何通过科技手段提升…

网络安全文章汇总导航(持续更新)

网络安全文章汇总导航&#xff08;持续更新&#xff09; 1. 介绍1.1. 初衷1.2. 更新时段1.3.最近更新时间及内容 2. 文章列表2.1. 基础篇2.2. 工具篇2.3. 靶场安装篇2.4. 权限提升篇2.5. 漏洞复现篇2.6. 加固与排查篇2.7. APP渗透篇2.8. 其它基础篇 1. 介绍 本章主要将博客中的…

ROS学习第十二节——话题通信控制小乌龟

1.基操一下 首先打开小乌龟程序和键盘控制程序 rosrun turtlesim turtlesim_node rosrun turtlesim turtle_teleop_key 查看话题列表 rostopic list 打开计算图查看具体是那个话题在起作用 rqt_graph 从上图可以看到两个节点之间的话题是 /turtle1/cmd_vel 使用以下命令获…

从零学习SDK(7)如何打包SDK

打包SDK的目的是为了方便将SDK提供给其他开发者或用户使用&#xff0c;以及保证SDK的兼容性和安全性。打包SDK可以有以下几个好处&#xff1a; 减少依赖&#xff1a;打包SDK可以将SDK所需的库、资源、文档等打包成一个文件或者一个目录&#xff0c;这样就不需要用户再去安装或…

直播app源码,流媒体自建好还是用第三方好

随着移动互联网的发展&#xff0c;直播应用已经成为人们日常生活中的一部分。但是&#xff0c;很多人在开发自己的直播app时&#xff0c;面临一个问题&#xff1a;自建直播流媒体服务器还是使用第三方直播平台&#xff1f;在本文中&#xff0c;我们将分析这两种选择的优缺点&am…

TLS简单介绍

第一篇是我同事讲的&#xff0c;第二篇在网上参考的。 两篇一起看&#xff0c;基本能搞懂TLS。 1、 概述 TLS&#xff08;Transport Layer Security&#xff0c;安全传输层)&#xff0c;TLS是建立在传输层TCP协议之上的协议&#xff0c;服务于应用层&#xff0c;它的前身是SS…

C# switch case语句入门and业务必知点

具体的语法形式如下。 switch(表达式) { case 值 1: 语句块 1; break; case 值 2: 语句块 2; break; ... default: 语句块 n; break; } 在这里&#xff0c;switch 语句中表达式的结果必须是整型、字符串…

2023年第一季度京东平台手机品牌销量排行榜

4月19日&#xff0c;调研机构Canalys发布了2023年第一季度的全球智能手机市场报告。根据数据显示&#xff0c;今年Q1全球智能手机市场份额TOP 5分别是三星&#xff08;22%&#xff09;、苹果&#xff08;21%&#xff09;、小米&#xff08;含Redmi&#xff0c;11%&#xff09;、…

git仓库

新的连接&#xff1a;将github账号或者gitee账号与可视化工具连接 操作仓库的大体过程&#xff1a; 连接之后将中央仓库里的东西&#xff0c;clone&#xff08;克隆&#xff09;到自己仓库中&#xff0c; 自己改完代码就push&#xff08;更新&#xff09;进中央仓库 连接之后…

JavaSE学习进阶day06_03 Collections类和Map集合

第三章 Collections类 3.1 Collections常用功能 java.utils.Collections是集合工具类&#xff0c;用来对集合进行操作。 常用方法如下&#xff1a; public static void shuffle(List<?> list):打乱集合顺序。 public static <T> void sort(List<T> list)…

Jenkins 在Windows下安装配置

下载 下载支持JDK1.8最后的版本&#xff0c;这个版本以上的都是JDK11&#xff0c;12的 https://mirrors.tuna.tsinghua.edu.cn/jenkins/war-stable/2.346.1/jenkins.war运行 进入目录&#xff0c;运行war java -jar jenkins.war如果你的JDK版本不支持的话就会报错了&#x…

蓝桥杯2023年第十四届省赛真题python A组 (个人的做题记录,没有全对,可以通过部分测试点)

试题 A: 特殊日期 本题总分&#xff1a;5 分 【问题描述】 记一个日期为 yy 年 mm 月 dd 日&#xff0c;统计从 2000 年 1 月 1 日到 2000000 年 1 月 1 日&#xff0c;有多少个日期满足年份 yy 是月份 mm 的倍数&#xff0c;同时也是 dd 的倍数。 【答案提交】 这是一道结果…