算法训练营day46|动态规划 part08:完全背包 (LeetCode 139. 单词拆分)

news2024/11/29 6:34:14

139. 单词拆分 (求排列方法)

题目链接🔥🔥
给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。

说明:
拆分时可以重复使用字典中的单词。
你可以假设字典中没有重复的单词。

示例 1:
输入: s = “leetcode”, wordDict = [“leet”, “code”]
输出: true
解释: 返回 true 因为 “leetcode” 可以被拆分成 “leet code”。

示例 2:
输入: s = “applepenapple”, wordDict = [“apple”, “pen”]
输出: true
解释: 返回 true 因为 “applepenapple” 可以被拆分成 “apple pen apple”。
注意你可以重复使用字典中的单词。

示例 3:
输入: s = “catsandog”, wordDict = [“cats”, “dog”, “sand”, “and”, “cat”]
输出: false

回溯思路分析

之前讲解回溯法专题的时候,讲过的一道题目回溯算法:分割回文串,就是枚举字符串的所有分割情况。
本道是枚举分割所有字符串,判断是否在字典里出现过。
回溯法C++代码:(会超时)

class Solution {
private:
    bool backtracking (const string& s, const unordered_set<string>& wordSet, int startIndex) {
        if (startIndex >= s.size()) {
            return true;
        }
        for (int i = startIndex; i < s.size(); i++) {
            string word = s.substr(startIndex, i - startIndex + 1);
            if (wordSet.find(word) != wordSet.end() && backtracking(s, wordSet, i + 1)) {
                return true;
            }
        }
        return false;
    }
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
        return backtracking(s, wordSet, 0);
    }
};

递归的过程中有很多重复计算,可以使用数组保存一下递归过程中计算的结果。

这个叫做记忆化递归,这种方法我们之前已经提过很多次了。

使用memory数组保存每次计算的以startIndex起始的计算结果,如果memory[startIndex]里已经被赋值了,直接用memory[startIndex]的结果。

C++代码如下:

class Solution {
private:
    bool backtracking (const string& s,
            const unordered_set<string>& wordSet,
            vector<bool>& memory,
            int startIndex) {
        if (startIndex >= s.size()) {
            return true;
        }
        // 如果memory[startIndex]不是初始值了,直接使用memory[startIndex]的结果
        if (!memory[startIndex]) return memory[startIndex];
        for (int i = startIndex; i < s.size(); i++) {
            string word = s.substr(startIndex, i - startIndex + 1);
            if (wordSet.find(word) != wordSet.end() && backtracking(s, wordSet, memory, i + 1)) {
                return true;
            }
        }
        memory[startIndex] = false; // 记录以startIndex开始的子串是不可以被拆分的
        return false;
    }
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
        vector<bool> memory(s.size(), 1); // -1 表示初始化状态
        return backtracking(s, wordSet, memory, 0);
    }
};

背包思路分析

单词就是物品,字符串s就是背包,单词能否组成字符串s,就是问物品能不能把背包装满。

拆分时可以重复使用字典中的单词,说明就是一个完全背包!

动规五部曲分析如下:

  1. 确定dp数组以及下标的含义

dp[i] : 字符串长度为i的话,dp[i]为true,表示可以拆分为一个或多个在字典中出现的单词。

  1. 确定递推公式

如果确定dp[j] 是true,且 [j, i] 这个区间的子串出现在字典里,那么dp[i]一定是true。(j < i )。

所以递推公式是 if([j, i] 这个区间的子串出现在字典里 && dp[j]是true) 那么 dp[i] = true。

  1. dp数组如何初始化

从递推公式中可以看出,dp[i] 的状态依靠 dp[j]是否为true,那么dp[0]就是递推的根基,dp[0]一定要为true,否则递推下去后面都都是false了。

那么dp[0]有没有意义呢?

dp[0]表示如果字符串为空的话,说明出现在字典里。

但题目中说了“给定一个非空字符串 s” 所以测试数据中不会出现i为0的情况,那么dp[0]初始为true完全就是为了推导公式。

下标非0的dp[i]初始化为false,只要没有被覆盖说明都是不可拆分为一个或多个在字典中出现的单词。

  1. 确定遍历顺序

题目中说是拆分为一个或多个在字典中出现的单词,所以这是完全背包。

还要讨论两层for循环的前后顺序。

如果求组合数就是外层for循环遍历物品,内层for遍历背包。
如果求排列数就是外层for遍历背包,内层for循环遍历物品

而本题其实我们求的是排列数,为什么呢。 拿 s = “applepenapple”, wordDict = [“apple”, “pen”] 举例。

“apple”, “pen” 是物品,那么我们要求 物品的组合一定是 “apple” + “pen” + “apple” 才能组成 “applepenapple”。

“apple” + “apple” + “pen” 或者 “pen” + “apple” + “apple” 是不可以的,那么我们就是强调物品之间顺序。

所以说,本题一定是 先遍历 背包,再遍历物品。

如果先遍历物品再遍历背包

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
        vector<bool> dp(s.size() + 1, false);
        dp[0] = true;
        for (int j = 0; j < wordDict.size(); j++) { // 物品
            for (int i = wordDict[j].size(); i <= s.size(); i++) { // 背包
                string word = s.substr(i - wordDict[j].size(), wordDict[j].size());
                // cout << word << endl;
                if ( word == wordDict[j] && dp[i - wordDict[j].size()]) {
                    dp[i] = true;
                }
                // for (int k = 0; k <= s.size(); k++) cout << dp[k] << " "; //这里打印 dp数组的情况 
                // cout << endl;
            }
        }
        return dp[s.size()];

    }
};

使用用例:s = “applepenapple”, wordDict = [“apple”, “pen”],对应的dp数组状态如下:
在这里插入图片描述
最后dp[s.size()] = 0 即 dp[13] = 0 ,而不是1,因为先用 “apple” 去遍历的时候,dp[8]并没有被赋值为1 (还没用"pen"),所以 dp[13]也不能变成1。

除非是先用 “apple” 遍历一遍,再用 “pen” 遍历,此时 dp[8]已经是1,最后再用 “apple” 去遍历,dp[13]才能是1。

  1. 举例推导dp[i]
    在这里插入图片描述

代码实现

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        set<string> dict(wordDict.begin(),wordDict.end());
        cout<<endl;
        vector<bool> dp(s.size()+1,false);
        dp[0]=true;
        for(int j=0;j<=s.size();j++){   // 遍历背包
            for(int i=0;i<j;i++){       // 遍历物品
                if(dict.find(s.substr(i,j-i))!=dict.end()&&dp[i]!=false){//substr(起始位置,截取的个数)
                    dp[j]=true;
                }
            }
        }
        return dp[s.size()];
    }
};

思考总结

再理解一下这里为什么是排列不是组合。


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

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

相关文章

AIGC之文本内容生成概述(下)—— GPT

GPT&#xff08;GenerativePre-TrainedTransformer&#xff09; 提到GPT模型&#xff0c;就不得不说众所周知的ChatGPT模型&#xff0c;ChatGPT的发展可以追溯到2018年&#xff0c;当时OpenAI发布了第一代GPT模型&#xff0c;即GPT-1&#xff0c;该模型采用Transformer结构和自…

Tableau自学四部曲_Part2:数据连接可视化原理

文章目录 一、数据连接1. 连接类型2. 连接方式3. 提取方式4. 连接筛选5. 保存类型6. 数据处理7. 理解本文的数据 二、可视化原理1. 做个小实验2. 数据是如何变成图表的&#xff1f;&#xff08;1&#xff09;数据变成图表的过程&#xff0c;就是用数据映射到视觉图形的过程&…

180B参数的Falcon登顶Hugging Face,最好开源大模型使用体验

文章目录 使用地址使用体验 使用地址 https://huggingface.co/spaces/tiiuae/falcon-180b-demo 使用体验 相比Falcon-7b&#xff0c;Falcon-180b拥有1800亿的参数量&#xff0c;在智能问答领域做到了Top 1。在回答问题的深度和广度上都明显优于只有70亿参数量的Falcon-7b&…

容器编排学习(二)镜像制作和私有仓库介绍

一 Dockerfile 1 概述 commit的局限 很容易制作简单的镜像&#xff0c;但碰到复杂的情况就十分不方便例如碰到下面的情况需要设置默认的启动命令需要设置环境变量需要指定镜像开放某些特定的端口 Dockerfile就是解决这些问题的方法 Dockerfile是一种更强大的镜像制作方式…

蓝牙方案|伦茨科技最新ESL蓝牙电子价签方案

随着电子货架标签市场无线标准的发布&#xff0c;蓝牙技术将协助零售业进行下一阶段的数字化转型&#xff0c;为商店和购物者提供更好的消费体验。电子货架标签提供了精准的自动定价&#xff0c;并增加了对实时促销和产品信息的访问&#xff0c;提供更令人满意且整合的全渠道体…

如何用Java编写代码来等待一个线程join()??

笔者在前面几篇文章中详细的讲解了&#xff1a;线程and进程的区别及其各种对比&#xff0c;如何中断一个线程等文章&#xff0c;接下来本篇文章主要讲解&#xff1a;用Java编写代码来等待一个线程join()&#xff1f;&#xff1f; 线程之间是并发执行的&#xff0c;操作系统对于…

清华开源LLM中英双语对话语言模型ChatGLM2,效果能赶超ChatGPT?

ChatGLM-6B 是一个开源的、支持中英双语的对话语言模型,基于 General Language Model (GLM) 架构,具有 62 亿参数.ChatGLM2-6B 是开源中英双语对话模型 ChatGLM-6B 的第二代版本,其第二代继承了第一代的优秀特点,并在第一代的基础上,更新了更多的新特性。 更强大的性能: C…

C语言——程序环境和预处理(再也不用担心会忘记预处理的知识)

了解程序环境和预处理 前言&#xff1a;一、程序环境二、编译链接2.1 翻译环境2.2 编译的几个阶段2.3 运行环境 三、预处理3.1 预定义符号3.2. #define的使用3.2.1 #define 定义标识符3.2.2 #define 定义宏3.2.3 #define 替换规则3.2.4 #和##的用途3.2.5 带副作用的宏参数3.2.6…

数据结构大作业 成绩分析c语言程序设计

界面加载 界面展示 成绩输入 求平均成绩 升序排列 降序排列 名字排序 按名字搜索 按ID搜索 每门课成绩分析 成绩单展示 -

1.6 二次曲面

二次曲面 文章目录 二次曲面知识点1 二次曲面的概念知识点2 常见的二次曲面知识点3 空间曲面小结 知识点1 二次曲面的概念 定义1 三元二次方程&#xff08;二次项系数不全为0&#xff09; A x 2 B y 2 C z 2 D x y E y z F z x G x H y I z J 0 Ax^2By^2Cz^2DxyEyzF…

Windows 12 网页版,开源了!

公众号关注 “GitHubDaily” 设为 “星标”&#xff0c;每天带你逛 GitHub&#xff01; 自 1985 年微软发布第一代 Windows 系统后&#xff0c;其市场占比一直逐步攀升。经过数十年的迭代发展&#xff0c;目前 Windows 已成为全球桌面操作系统的主导者。 今年整个科技行业开始拥…

Canal使用

什么是Canal Canal 主要用途是基于 MySQL 数据库增量日志解析&#xff0c;提供增量数据订阅和消费。 早期阿里巴巴因为杭州和美国双机房部署&#xff0c;存在跨机房同步的业务需求&#xff0c;实现方式主要是基于业务 trigger 获取增量变更。从 2010 年开始&#xff0c;业务逐…

杭州快递物流展-2024长三角快递物流供应链与技术装备展览会(杭州)

2024快递物流创新高质量发展论坛暨 2024长三角快递物流供应链与技术装备展览会(杭州) 时间&#xff1a;2024年4月12-14 日 地点&#xff1a;杭州国际博览中心 ESYE长三角快递物流展是亚洲范围内超大规模的快递物流业展示平台&#xff0c;由于展会的需求 及扩大市场的影响力…

【Tools】如何在VSCode上使用C++ 保姆教程

VSCode&#xff08;Visual Studio Code&#xff09;是一款轻量级的代码编辑器&#xff0c;拥有丰富的插件生态系统&#xff0c;支持多种语言开发。在本文中&#xff0c;将介绍如何在 VSCode 中配置 C 环境&#xff0c;以便于开发和调试 C 代码。 VSCode 具有以下优势&#xff1…

山寨能走多远?盗版还是盗火?KCC@深圳活动圆满举办

2023.8.19 &#xff0c;我们组织了一场大家期待已久的线下活动&#xff1a; KCC深圳&#xff1a;Kickoff & 读书会Meetup 来了深圳都是深圳人&#xff0c;没有哪座城市能够像深圳代表中国改革开放的发展历程&#xff0c;年轻、上进、有活力、拥抱新事物&#xff0c;同样&am…

Java版本企业工程行业管理系统源码

Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下&#xff1a; 首页 工作台&#xff1a;待办工作、消息通知、预警信息&#xff0c;点击可进入相应的列表 项目进度图表&#xff1a;选择&#xff08;总体或单个&#xff09;项目显示1…

直接接入电商API接口实现调用封装好的商品详情SKU数据参数及返回

什么是API&#xff1f; API全称为Application Programming Interface&#xff0c;中文是应用程序编程接口。它其实是一些预先定义的函数&#xff0c;目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力&#xff0c;而又无需访问源码&#xff0c;或理解内部工…

02_瑞萨GUI(LVGL)移植实战教程之创建第一个工程并适配串口打印功能

本系列教程配套出有视频教程&#xff0c;观看地址&#xff1a;https://www.bilibili.com/video/BV1gV4y1e7Sg # 2. 创建第一个工程适配串口打印功能 本次实验我们通过创建一个简单的工程&#xff0c;在其基础上完成串口打印功能&#xff0c;从而熟悉 e2stduio&#xff08;Rene…

linux系统中固化和更新uboot、zImage和dtb方法(经典)

​ 大家好&#xff0c;今天给大家介绍一下imx6ull固化和更新uboot、zImage和dtb方法总结&#xff0c;希望这篇文章对大家有所帮助。 进行固化和更新的前提&#xff0c;uboot.imx、zImage、imx6ull.dtb和rootfs已经编译好&#xff0c;并且能成功启动和挂载。 在讲解imx6ull固…

2023年8月Web3行业月度发展报告区块链篇 | 陀螺科技会员专享

8月&#xff0c;加密市场的核心叙事未变&#xff0c;仍旧围绕比特币现货ETF展开&#xff0c;无论是美国最大支付机构Paypal发布稳定币或是月底灰度胜诉SEC&#xff0c;均以此为利基推动市场的局部回升&#xff0c;月底灰度案一度刺激BTC拉升至28000美元。但由于SEC方面态度表现…