93. 复原 IP 地址(力扣LeetCode)

文章目录

  • 93. 复原 IP 地址
    • 题目描述
    • 回溯算法
    • 回溯优化(在原s字符串上操作)

93. 复原 IP 地址

题目描述

有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。

例如:“0.1.2.201” 和 “192.168.1.1” 是 有效 IP 地址,但是 “0.011.255.245”、“192.168.1.312” 和 “192.168@1.1” 是 无效 IP 地址。
给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 ‘.’ 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。

示例 1:

输入:s = “25525511135”
输出:[“255.255.11.135”,“255.255.111.35”]

示例 2:

输入:s = “0000”
输出:[“0.0.0.0”]

示例 3:

输入:s = “101023”
输出:[“1.0.10.23”,“1.0.102.3”,“10.1.0.23”,“10.10.2.3”,“101.0.2.3”]

提示:

  • 1 <= s.length <= 20
  • s 仅由数字组成

回溯算法

注意:这段代码中使用了一个string path来存入当前正在构建的 IP 地址,存的时候是如果符合条件就直接存入了,所以回溯的时候要整段的回溯,即整段的删除

正确如下:

// 保存当前路径,以便之后进行恢复
string temp = path;//符合条件就整段存入
path += s.substr(start, i - start + 1);
path += '.';// 继续递归,尝试找到下一部分
backstracking(s, i + 1, count);// 整段回溯,恢复到上一步的路径和点的数量
path = temp;

错误回溯如下:

path+=(s.substr(start,i-start+1));
path+='.';
backstracking(s,i+1,count);
if(path.back()=='.')path.pop_back();
path.pop_back();

举例说明:

假设给定的字符串为 "25525511135",并且我们正在尝试构造有效的IP地址。我们从空路径path开始并逐步构建它。

当我们第一次调用backstracking函数时,path为空,count为0。

我们逐步试探这个字符串的不同部分,看看是否可以构成IP地址的一个段。例如,我们首先尝试 "255" 作为IP地址的第一个部分,所以path变为 "255." 并且count变为1。

现在我们递归调用backstracking,它会继续向path中添加新的段。如果我们再次添加 "255"path现在就是 "255.255." 并且count是2。

继续这个递归过程,让我们假设第三段是 "11",那么path会变成 "255.255.11." 并且count是3。

然后我们添加最后一个段 "135"path完成为 "255.255.11.135"。现在我们找到了一个有效的IP地址,它会被添加到result中。

在添加到result之后,我们需要返回到上一个状态,也就是 "255.255.11.",以便尝试其他可能的IP地址段。为了做到这一点,我们需要恢复path到它在添加最后一个段之前的状态。这就是所谓的回溯,它允许我们“撤销”最后一步并尝试另一条路。

在原始代码中,作者尝试用path.pop_back()来实现这一点,但这种方法不可靠,因为它只能移除path末尾的一个字符。如果最后一个段包含多个字符,比如 "135",单纯调用pop_back()一次并不能恢复到 "255.255.11."。这就是为什么我们需要记录当前状态到一个临时变量(在这里是temp),并在递归调用返回后,使用这个临时变量来恢复path

正确的做法是在递归前保存path的当前值,然后无论添加了多少字符,在递归结束后都能正确地恢复原始状态。这就是为什么我们在上面的修正代码中使用:

string temp = path;
// ...在递归中添加字符到path...
path = temp; // 在递归结束后恢复path的原始状态

这样无论path添加了多少个字符,我们都能通过简单地将path重新赋值为temp来恢复到递归调用之前的状态。

完整代码如下:

class Solution {
public:// 主函数,接受一个字符串作为输入,返回所有可能的有效 IP 地址vector<string> restoreIpAddresses(string s) {backstracking(s, 0, 0);  // 调用辅助函数开始回溯过程return result;  // 返回存储所有有效 IP 地址的结果}private:vector<string> result;  // 存储所有可能的有效 IP 地址string path;  // 存储当前正在构建的 IP 地址// 辅助函数,用于检查字符串 s 的一个子串是否可以成为 IP 地址的一部分bool cheak(string& s, int begin, int end) {// 如果开始位置大于结束位置,返回falseif (begin > end) return false;// 如果子串以 '0' 开头且长度大于1,返回false(防止前导0)if (s[begin] == '0' && begin != end) return false;// 用于计算子串的数值int sum = 0;for (int i = begin; i <= end; i++) {// 如果字符不是数字,返回falseif (s[i] > '9' || s[i] < '0') return false;// 将字符转换为数字并累加到 sumsum = sum * 10 + (s[i] - '0');// 如果累计的数值大于255,返回false(IP地址的每部分应在0-255之间)if (sum > 255) return false;}// 子串是IP地址的一个有效部分return true;}// 辅助函数,用于进行回溯void backstracking(string& s, int start, int count) {// 如果已经找到3个点,此时应检查最后一部分是否有效if (count == 3) {// 检查最后一部分是否有效if (cheak(s, start, s.size() - 1)) {// 如果有效,将其添加到当前路径末尾path += s.substr(start);// 将当前完整路径加入到结果集result.push_back(path);}// 无论最后一部分是否有效,到此为止都应该返回return;}// 遍历字符串的每一部分for (int i = start; i < s.size(); i++) {// 检查从start到i的子串是否有效if (cheak(s, start, i)) {// 保存当前路径,以便之后进行恢复string temp = path;// 将有效的子串加入到当前路径path += s.substr(start, i - start + 1);// 加入点分隔符path += '.';count++;  // 增加点的数量,表示找到了IP地址的一部分// 继续递归,尝试找到下一部分backstracking(s, i + 1, count);// 回溯,恢复到上一步的路径和点的数量path = temp;count--;} else {// 一旦发现子串不再有效,就中断循环break;}}}
};

这段代码实现了一个回溯算法,用以搜索并构建所有可能的有效IP地址组合。主函数restoreIpAddresses初始化回溯过程,私有成员result和path分别用来存储最终结果和当前构造的IP地址。私有函数cheak用于验证给定字符串的子串是否为有效的IP地址段,而backstracking是一个递归函数,负责穷举并检查所有可能的IP地址组合。

回溯优化(在原s字符串上操作)

// 93. 复原 IP 地址
class Solution {
public:// 主函数,接受一个字符串作为输入,返回所有可能的有效 IP 地址vector<string> restoreIpAddresses(string s) {// 如果字符串长度不适合构成IP地址,直接返回空结果if(s.size() < 4 || s.size() > 12) return result;// 开始回溯搜索过程backstracking(s, 0, 0);// 返回找到的所有有效IP地址return result;}private:// 用于存储所有找到的有效IP地址的结果数组vector<string> result;// 辅助函数,用于检查字符串s的部分是否可以成为IP地址的一部分// start为部分的起始索引,end为部分的结束索引bool check(string& s, int start, int end) {// 如果起始索引大于结束索引,表示这是一个空部分,返回falseif(start > end) return false;// 如果部分以'0'开头并且长度大于1,表示有前导零,返回falseif(s[start] == '0' && start != end) return false;// 用于计算部分的数值int sum = 0;for(int i = start; i <= end; i++) {// 如果字符不是数字,返回falseif(s[i] < '0' || s[i] > '9') return false;// 把字符转为数字并加到sumsum = sum * 10 + (s[i] - '0');// 如果数值大于255,返回falseif(sum > 255) return false;}// 部分是IP地址的一个有效段落return true;}// 一个递归函数,用于进行回溯搜索// s是原始字符串的引用,start是当前处理的起始位置,count是到目前为止添加的点分隔符数量void backstracking(string& s, int start, int count) {// 如果已经添加了3个点分隔符if(count == 3) {// 检查剩余部分是否构成有效的IP地址的最后一部分if(check(s, start, s.size() - 1)) {// 如果是,添加到结果中result.push_back(s);}// 已经检查完最后一部分,返回上一层return;}// 遍历字符串的每个可能的部分for(int i = start; i < s.size(); i++) {// 检查当前部分是否有效if(check(s, start, i)) {// 在当前部分的下一个位置插入点分隔符s.insert(s.begin() + i + 1, '.');// 递归调用,处理下一部分,起始位置是当前位置的下两个(因为添加了点分隔符)// 并增加点分隔符的数量backstracking(s, i + 2, count + 1);// 回溯:删除刚刚添加的点分隔符,恢复字符串s.erase(s.begin() + i + 1);} else {// 一旦找到无效部分,终止循环break;}}}
};

此代码定义了一个名为Solution的类,其包含用于找到所有有效IP地址的方法。这个问题的关键在于能够通过插入点分隔符来划分给定的数字字符串。首先,它会在主函数中检查输入字符串s的长度是否符合构造IP地址的基本要求。然后,使用递归回溯函数backstracking来尝试所有可能的点分隔符插入位置,从而形成有效的IP地址段。check函数用来验证每一段是否符合IP地址的规则。当构造完一个IP地址(即插入了3个点)后,它会在result数组中记录下来。最终,主函数返回包含所有可能有效IP地址的result数组。

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

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

相关文章

真不愧是华为出来的,真的太厉害了。。。

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 实习去了博彦科技&#xff08;外包&#xff09;&#xff0c;做的就是螺丝钉的活&#xff0c;后面…

华为---MSTP(一)---MSTP生成树协议

目录 1. MSTP技术产生背景 2. STP/RSTP的缺陷 ​编辑 2.1 无法均衡流量负载 2.2 数据使用次优路径 3. MSTP生成树协议 3.1 MSTP相关概念 3.2 MSTP树生成的形成过程 4. MSTP报文 1. MSTP技术产生背景 RSTP在STP基础上进行了改进&#xff0c;实现了网络拓扑快速收敛。但…

chisel入门初步2_2——-1/2次方生成器

由之前的GCN网络的介绍可以得知&#xff0c;我们需要输入两个乘数&#xff08;两个节点的节点度&#xff09;&#xff0c;并输出他们乘积的-1/2次方&#xff0c;此处由于当时设计的booth编码的乘法器为有符号数&#xff0c;而此处是无符号数&#xff0c;实在懒得再写一份了&…

SpringBoot+Maven项目打包

项目的主POM文件里面添加maven打包插件 <build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.2</version><configuration><sour…

推荐一款新的自动化测试框架:DrissionPage

今天给大家推荐一款基于Python的网页自动化工具&#xff1a;DrissionPage。这款工具既能控制浏览器&#xff0c;也能收发数据包&#xff0c;甚至能把两者合而为一&#xff0c;简单来说&#xff1a;集合了WEB浏览器自动化的便利性和 requests 的高效率优点。 一、DrissionPage框…

【C++庖丁解牛】默认成员函数

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 目录 前言1. 构造函数1.1 …

android系统签名

系统签名是指由 Android 系统或设备制造商使用他们的私钥对应用程序进行数字签名的过程。在 Android 应用程序开发中&#xff0c;应用程序的 APK 文件需要使用开发者的私钥进行签名&#xff0c;以便在安装和更新时验证应用程序的真实性和完整性。 系统签名是一种特殊的签名&am…

职场高薪 |「中高级测试」面试题

【软件测试面试突击班】2024吃透软件测试面试最全八股文攻略教程&#xff0c;一周学完让你面试通过率提高90%&#xff01;&#xff08;自动化测试&#xff09; 一.基础题 1.测试用例你一般是怎么设计的&#xff0c;怎么可以提高覆盖率&#xff1f; 有没有形成自己的 一套方法论…

【个人感悟】医院所见思考怎么做AI+医疗

今天陪家里人去医院折腾了一上午&#xff0c;从消化科检查&#xff08;验血、胸部CT、心电图&#xff09;&#xff0c; 消化科医生看完报告&#xff0c;说CT影像看肺部有些问题&#xff0c;又排队挂号呼吸科折腾&#xff0c; 一上午来回就过去了。 整个过程看似系统信息化程度…

Docker运行时安全之道: 保障容器环境的安全性

引言 Docker作为容器化技术的领军者,为应用部署提供了灵活性和便捷性。然而,在享受这些优势的同时,必须重视Docker运行时的安全性。本文将深入研究一些关键的Docker运行时安全策略,以确保你的容器环境在生产中得到有效的保护。 1. 使用最小特权原则 保持容器以最小权限运…

Jmeter接口测试---随机数、加密、cookie鉴权、断言、CSV参数化

随机数 第一步&#xff1a;选择工具-函数助手对话框 第二步&#xff1a;选择random&#xff0c;设置最大值最小值&#xff0c;复制函数字符串到指定位置 加密接口 类型&#xff1a;AES、DES、Base64、RSA&#xff08;可以解密&#xff09; | MD5、SHA、HmacSHA&#xff08;不…

llama.c代码2

1、forward 1.1、复习 encode(tokenizer, prompt, 1, 0, prompt_tokens, &num_prompt_tokens); 在encode函数结尾处(gdb) p *n_tokens $3 2(gdb) p *tokens3 $6 {1, 22172, 417} 在encode调用后 (gdb) print num_prompt_tokens $11 2 (gdb) print *prompt_tokens3 $13 …

【Nginx基础和原理介绍】讲解

Nginx基础和原理介绍 1. 前言2. 基本特性3. 工作原理4. 总结 1. 前言 Nginx&#xff08;发音为“engine-x”&#xff09;是一个高性能的HTTP和反向代理服务器&#xff0c;它还可以作为IMAP/POP3代理服务器使用&#xff0c;Nginx是由Igor Sysoev为俄罗斯访问量第二的Rambler.ru…

JavaScript 中的类型转换机制(详细讲解)

文章目录 一、概述二、显示转换Number()parseInt()String()Boolean() 三、隐式转换自动转换为布尔值自动转换成字符串自动转换成数值 一、概述 前面我们讲到&#xff0c;JS中有六种简单数据类型&#xff1a;undefined、null、boolean、string、number、symbol&#xff0c;以及…

(sub)三次握手四次挥手

TCP的三次握手与四次挥手理解及面试题 序列号seq&#xff1a;占4个字节&#xff0c;用来标记数据段的顺序&#xff0c;TCP把连接中发送的所有数据字节都编上一个序号&#xff0c;第一个字节的编号由本地随机产生&#xff1b;给字节编上序号后&#xff0c;就给每一个报文段指派一…

(学习日记)2024.03.03:UCOSIII第五节:常用汇编指令+OS初始化+启动任务+任务切换

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

双重检验锁

双重检验锁&#xff1a;设计模式中的单例模式&#xff0c;细分为单例模式中的懒加载模式。 单例模式 单例模式&#xff1a;指的是一个类只有一个对象。最简单的实现方式是设一个枚举类&#xff0c;只有一个对象。缺点是当对象还没有被使用时&#xff0c;对象就已经创建存在了…

Backend - 日志记录

目录 1. settings.py 文件设定 2. book_log.py 文件设定 3. view 视图文件调用 1. settings.py 文件设定 文件位置&#xff1a;BookProject 目录下 LOG_PATH os.path.join(os.getcwd(), logs) # 设定日志文件位置&#xff1a;项目名下的logs文件夹中 2. book_log.py 文…

C++ 创建并初始化对象

创建并初始化C对象 当我们创建一个C对象时&#xff0c;它需要占用一些内存&#xff0c;即使我们写一个完全为空的类&#xff0c;类中没有成员&#xff0c;什么也没有&#xff0c;它至少也要占用一个字节的内存。但是我们类中有很多成员&#xff0c;它们需要存储在某地方&#…

【扩散模型】生成模型中的Residual Self-Attention UNet 以及 DDPM的pytorch代码

参考&#xff1a; [1] https://github.com/xiaohu2015/nngen/blob/main/models/diffusion_models/ddpm_cifar10.ipynb [2] https://www.bilibili.com/video/BV1we4y1H7gG/?spm_id_from333.337.search-card.all.click&vd_source9e9b4b6471a6e98c3e756ce7f41eb134 TOC 1 UNe…