【Py/Java/C++三种语言详解】LeetCode每日一题240109【动态规划】LeetCode2707题、字符串中的额外字符

这篇具有很好参考价值的文章主要介绍了【Py/Java/C++三种语言详解】LeetCode每日一题240109【动态规划】LeetCode2707题、字符串中的额外字符。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

题目描述

给你一个下标从 0 开始的字符串 s 和一个单词字典 dictionary 。你需要将 s 分割成若干个 互不重叠 的子字符串,每个子字符串都在 dictionary 中出现过。s 中可能会有一些 额外的字符 不在任何子字符串中。

请你采取最优策略分割 s ,使剩下的字符 最少

示例 1:

输入:s = “leetscode”, dictionary = [“leet”,“code”,“leetcode”]
输出:1
解释:将 s 分成两个子字符串:下标从 0 到 3 的 “leet” 和下标从 5 到 8 的 “code” 。只有 1 个字符没有使用(下标为 4),所以我们返回 1 。

示例 2:

输入:s = “sayhelloworld”, dictionary = [“hello”,“world”]
输出:3
解释:将 s 分成两个子字符串:下标从 3 到 7 的 “hello” 和下标从 8 到 12 的 “world” 。下标为 0 ,1 和 2 的字符没有使用,所以我们返回 3 。

提示:

  • 1 <= s.length <= 50
  • 1 <= dictionary.length <= 50
  • 1 <= dictionary[i].length <= 50
  • dictionary[i]s 只包含小写英文字母。
  • dictionary 中的单词互不相同。

解题思路

比较典型的字符串序列dp问题,类似题目包括LeetCode137、单词拆分,LeetCode472、连接词 。

这类问题的核心点在于思考动态转移过程。

简单举例

以示例二为例,已知s的子字符串"say"不位于dictionary中,而接下来的一个子字符串"hello"位于dictionary中。

s的以"o"为结尾的子字符串"sayhello"可以由以"y"为结尾的子字符串"say"和接下来的"hello"拼接构成。

那么切割"sayhello"的剩余字符数,和切割"say"的剩余字符串相等。

哈希集合进行查找

由于涉及到子串的查找,故将数组dictionary转化为哈希集合word_set,方便在O(1)的复杂度内完成查找。即

word_set = set(dictionary)

动态规划三部曲

我们考虑动态规划三部曲:

  1. dp数组的含义是什么?
  • 构建长度为(n+1)dp数组
  • dp[i]表示以s[i-1]为结尾的子字符串s[:i],分割后剩下的字符的最少数目(即题目设问)。
  • dp[0]表示空串""的情况。
  1. 动态转移方程是什么?
  • 使用双重循环枚举所有子串s[i:j],即枚举子串的终点j和起点i
  • 考虑子串s[:i]和子串s[:j]之间的关系(i < j
    • 如果子串s[i:j]位于word_set中。则s[:j]可以由s[:i]加上s[i:j]构成
      • dp[j]可以由dp[i]转移过来。
      • 存在dp[j] = min(dp[i], dp[j])
    • 如果子串s[i:j]不位于word_set中。则可认为s[:j]可以由s[:i]加上子串s[i:j]中的每一个单个字符构成,s[i:j]中一共存在j-i个单字符。
      • dp[j]可以由dp[i]+j-i转移过来
      • 存在dp[j] = min(dp[i]+j-i, dp[j])

上述逻辑整理为代码即

for j in range(1, (n+1)):
    for i in range(j):
        if s[i:j] in word_set:
            dp[j] = min(dp[i], dp[j])
        else:
            dp[j] = min(dp[i]+j-i, dp[j])
  1. dp数组如何初始化?
  • 初始化dp[i] = i,表示在初始状态下,每一个字符都进行分割。对于以第i个字符为结尾的子串s[:i],一共分割出i个字符。
dp = [i for i in range(n+1)]

代码

python

# dp:O(N^3)复杂度
class Solution:
    def minExtraChar(self, s: str, dictionary: List[str]) -> int:
        word_set = set(dictionary)
        n = len(s)
        # 初始化长度为(n+1)的dp数组
        # dp[i]表示以s[i-1]为结尾的子字符串s[:i]
        # 分割后剩下的字符的最少数目
        # 初始化dp[i] = i,表示每一个字符都进行分割
        dp = [i for i in range(n+1)]
        # 遍历结束位置j
        for j in range(1, (n+1)):
            # 遍历子字符串的开始位置i
            for i in range(j):
                # 如果子串s[i:j]位于word_set中
                # 则s[:j]可以由s[:i]加上s[i:j]构成
                # 故dp[j]可以由dp[i]转移过来
                if s[i:j] in word_set:
                    dp[j] = min(dp[i], dp[j])
                # 否则,可认为s[:j]可以由s[:i]加上子串s[i:j]中的j-i个单个字符构成
                # 故dp[j]可以由dp[i]+j-i转移过来
                else:
                    dp[j] = min(dp[i]+j-i, dp[j])
        # 最后返回dp[n]
        return dp[n]

java

import java.util.HashSet;
import java.util.Set;

class Solution {
    public int minExtraChar(String s, String[] dictionary) {
        Set<String> wordSet = new HashSet<>();
        for (String word : dictionary) {
            wordSet.add(word);
        }
        
        int n = s.length();
        int[] dp = new int[n + 1];
        for (int i = 0; i <= n; i++) {
            dp[i] = i;
        }
        
        for (int j = 1; j <= n; j++) {
            for (int i = 0; i < j; i++) {
                if (wordSet.contains(s.substring(i, j))) {
                    dp[j] = Math.min(dp[i], dp[j]);
                } else {
                    dp[j] = Math.min(dp[i] + j - i, dp[j]);
                }
            }
        }
        
        return dp[n];
    }
}

cpp

#include <vector>
#include <string>
#include <unordered_set>
using namespace std;

class Solution {
public:
    int minExtraChar(string s, vector<string>& dictionary) {
        unordered_set<string> wordSet(dictionary.begin(), dictionary.end());
        int n = s.length();
        vector<int> dp(n + 1, 0);
        for (int i = 0; i <= n; i++) {
            dp[i] = i;
        }
        
        for (int j = 1; j <= n; j++) {
            for (int i = 0; i < j; i++) {
                if (wordSet.find(s.substr(i, j - i)) != wordSet.end()) {
                    dp[j] = min(dp[i], dp[j]);
                } else {
                    dp[j] = min(dp[i] + j - i, dp[j]);
                }
            }
        }
        
        return dp[n];
    }
};

时空复杂度

时间复杂度:O(N^3)。枚举子串的终点j和起点i需要双重for循环,复杂度为O(N^2),获得子串切片s[i:j]的时间复杂度也为O(N)。故总时间复杂度为O(N^3)Ns的长度。

空间复杂度:O(M)word_set所需空间,M为dictionary的长度。


华为OD算法/大厂面试高频题算法练习冲刺训练

  • 华为OD算法/大厂面试高频题算法冲刺训练目前开始常态化报名!目前已服务100+同学成功上岸!

  • 课程讲师为全网50w+粉丝编程博主@吴师兄学算法 以及小红书头部编程博主@闭着眼睛学数理化

  • 每期人数维持在20人内,保证能够最大限度地满足到每一个同学的需求,达到和1v1同样的学习效果!

  • 60+天陪伴式学习,40+直播课时,300+动画图解视频,300+LeetCode经典题,200+华为OD真题/大厂真题,还有简历修改、模拟面试、专属HR对接将为你解锁

  • 可上全网独家的欧弟OJ系统练习华子OD、大厂真题

  • 可查看链接 大厂真题汇总 & OD真题汇总(持续更新)

  • 绿色聊天软件戳 od1336了解更多文章来源地址https://www.toymoban.com/news/detail-792880.html

到了这里,关于【Py/Java/C++三种语言详解】LeetCode每日一题240109【动态规划】LeetCode2707题、字符串中的额外字符的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【LeetCode】每日一题 -- 1240. 铺瓷砖 -- Java Version

    题目链接 :https://leetcode.cn/problems/tiling-a-rectangle-with-the-fewest-squares/ 23.05.31 华为机试第二题 NP-Complete 问题 题解参考:Java DFS暴力递归(详细注释) … 题解思路 : 检查当前答案是否大于等于当前最佳答案,若是,则进行剪枝,回溯 检查正方形中是否有空位,若无空位,更新

    2024年02月08日
    浏览(34)
  • leetcode每日一题44

    图论 dfs/bfs dfs代码框架 思路:本题要求找到被x围绕的陆地,所以边界的陆地O肯定不符合条件。那么我们只要从周边找到陆地O然后 通过 dfs或者bfs 将周边靠陆地且相邻的陆地O都变成A,然后再去重新遍历地图的时候,把剩下的O变成X,再把所有的A变成O。 确认递归函数,参数

    2024年01月19日
    浏览(45)
  • Leetcode每日一题——“移除元素”

    各位CSDN的uu们你们好呀,小雅兰又来啦,今天,小雅兰的内容是移除元素,下面,让我们进入Leetcode的世界吧   说明: 为什么返回数值是整数,但输出的答案是数组呢? 请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。 你可以

    2023年04月23日
    浏览(54)
  • 每日一题(LeetCode)----二分查找(一)

    给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 示例 2: 示例 3: 提示: 1 = nums.length = 104 -104 = nums[i] = 104 nums 为 无重复元素 的 升序 排列数

    2024年02月08日
    浏览(50)
  • 每日一题:LeetCode-75. 颜色分类

    前言: 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈    🔎🔎如果说代码有灵魂,那么它的灵魂一定是👉👉 算法 👈👈,因此,想要写出💚优美的程序💚,核心算法是必不可少的,少年,你渴望力量吗😆😆,想掌握程序的灵魂吗❓❗️那么就必须踏上这样一条漫长

    2024年02月04日
    浏览(40)
  • 每日一题:leetcode 57 插入区间

    给你一个  无重叠的  , 按照区间起始端点排序的区间列表。 在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。 示例 1: 示例 2: 示例 3: 示例 4: 示例 5: 提示: 0 = intervals.length = 104 intervals[i].length == 2 0 = int

    2024年02月11日
    浏览(46)
  • 【每日一题】Leetcode - 283. 移动零

    Leetcode - 283. 移动零 从右向左遍历,遇到0,就将后面所有元素前移,同时更新长度,使其减1,因为移动n次,倒数n位就被0占据,后续操作可忽略 前面的操作,从右向左,每次遇到0移动都要跑半个数组,慢在整体前移; 换个思路,从左向右,记录首个0的位置,将后面的第一

    2024年02月11日
    浏览(45)
  • 【LeetCode每日一题】——566.重塑矩阵

    矩阵 简单 566.重塑矩阵 在 MATLAB 中,有一个非常有用的函数 reshape ,它可以将一个 m x n 矩阵重塑为另一个大小不同(r x c)的新矩阵,但保留其原始数据。 给你一个由二维数组 mat 表示的 m x n 矩阵,以及两个正整数 r 和 c ,分别表示想要的重构的矩阵的行数和列数。 重构后

    2024年02月14日
    浏览(47)
  • 【LeetCode每日一题】——575.分糖果

    哈希表 简单 575.分糖果 Alice 有 n 枚糖,其中第 i 枚糖的类型为 candyType[i] 。Alice 注意到她的体重正在增长,所以前去拜访了一位医生。 医生建议 Alice 要少摄入糖分,只吃掉她所有糖的 n / 2 即可(n 是一个偶数)。Alice 非常喜欢这些糖,她想要在遵循医生建议的情况下,尽可

    2024年02月13日
    浏览(76)
  • 【LeetCode每日一题】——85.最大矩形

    矩阵 困难 85.最大矩形 给定一个仅包含 0 和 1 、大小为 rows x cols 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。 示例 1: 输入:matrix = [[“1”,“0”,“1”,“0”,“0”],[“1”,“0”,“1”,“1”,“1”],[“1”,“1”,“1”,“1”,“1”],[“1”,“0”,“0”,“1”,“

    2024年02月13日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包