当前位置: 首页 > article >正文

[动态规划 滑动窗口]

1. 定义DP状态(核心思路)

问题分析


word1 转换为 word2,每个操作对应状态转移。定义 dp[i][j] 表示将 word1[0..i-1] 转换为 word2[0..j-1] 的最小操作数。

2. 初始化DP表

目的:处理空字符串的边界情况。

3. 填充DP表(状态转移方程)

状态转移逻辑

  • 若 word1[i-1] == word2[j-1]:无需操作,直接继承左上方值 → dp[i][j] = dp[i-1][j-1]
  • 否则:取三种操作的最小值 + 1
    dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
class Solution {
public:
    int minDistance(string word1, string word2) {
        int m = word1.size(), n = word2.size();
        vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));

        // 定义 dp[i][j] 表示将 word1[0..i-1] 转换为 word2[0..j-1] 的最小操作数。
        for (int i = 0; i <= m; ++i) dp[i][0] = i;
        for (int j = 0; j <= n; ++j) dp[0][j] = j;

        for (int i = 1; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                if (word1[i - 1] == word2[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1];
                } else {
                    dp[i][j] = min({dp[i - 1][j], dp[i][j-1], dp[i-1][j-1]}) + 1;
                }
            }
        }
        return dp[m][n];
    }
    
};

关键点解析

  1. 时间复杂度与空间复杂度

    • 时间复杂度:O(mn)(双重循环)
    • 空间复杂度:O(mn)(二维DP表)
  2. 状态转移方程的理解

    • 删除操作dp[i-1][j] → 删除 word1 的第i个字符后,匹配到 word2 的前j个字符
    • 插入操作dp[i][j-1] → 插入 word2 的第j个字符后,匹配到 word2 的前j个字符
    • 替换操作dp[i-1][j-1] → 将 word1 的第i个字符替换为 word2 的第j个字符

1. 处理特殊情况

目的:处理空字符串或无效输入,直接返回空结果。

2. 初始化字符计数器

目的:统计目标字符串 t 的字符需求,并为滑动窗口建立字符统计。
实现步骤

  1. 使用哈希表(或数组)记录 t 中每个字符的出现次数。
  2. 定义窗口哈希表,动态记录当前窗口内字符的出现次数。
  3. 初始化有效计数 valid,用于判断窗口是否覆盖 t 的所有字符。

3. 滑动窗口主体框架

目的:用双指针维护窗口,右指针扩展窗口,左指针收缩窗口。
实现步骤

  1. 定义左右指针 left=0, right=0,初始化最小窗口长度和起始位置。
  2. 右指针遍历字符串,更新窗口统计。
  3. 当窗口满足覆盖条件时,收缩左指针寻找最小窗口。

4. 右指针扩展逻辑

目的:更新窗口字符计数,并检查是否满足 t 的需求。
关键点

  • 仅处理 t 中需要的字符。
  • 当窗口内某字符数量达到 t 的需求时,valid 自增。

5. 判断窗口覆盖条件

目的:当 valid 等于 need 的字符种类数时,说明窗口已覆盖 t

6. 左指针收缩逻辑

目的:收缩窗口左边界,尝试找到更小的覆盖子串。
关键点

  • 左移时需检查移出的字符是否影响 valid
  • 更新窗口统计时需反向操作(与右指针扩展相反)。
class Solution {
public:
    string minWindow(string s, string t) {
        if (s.empty() || t.empty() || s.length() < t.length()) return "";

        unordered_map<char, int> need, window;
        for (char c : t) need[c]++;
        int valid = 0;

        int left = 0, right = 0;
        int start = 0, min_len = INT_MAX;

        while (right < s.size()) {
            // 右指针扩展
            char c = s[right++];

            if (need.count(c)) {
                window[c]++;
                if (window[c] == need[c]) {
                    valid++;
                }
            }

            while (valid == need.size()){
                if (right - left < min_len) {
                    min_len = right - left;
                    start = left;
                }

                char d = s[left++];
                if (need.count(d)) {
                    if (window[d] == need[d]) {
                        valid--;
                    }
                    window[d]--;
                }
            }
        }
        return min_len == INT_MAX ? "" : s.substr(start, min_len);
    }
};

1. 处理特殊情况

目的:处理金额为0或硬币数组为空的情况,直接返回结果。

2. 初始化动态规划数组

目的:定义 dp[i] 表示凑成金额 i 所需的最少硬币数,初始化为不可达值(amount + 1),并将 dp[0] 设为0。

3. 状态转移填充

目的:遍历每个硬币面额,更新所有可能金额的最小硬币数。
实现步骤

  1. 外层循环遍历每个硬币面额。
  2. 内层循环从当前硬币面额开始,到目标金额 amount 结束,更新 dp[j]

状态转移方程

  • dp[j] = min(dp[j], dp[j - coin] + 1)
    表示使用当前硬币 coin 时,金额 j 的最优解可能来自 j - coin 的解加1 

4. 处理结果

目的:判断最终结果是否有效,返回最少硬币数或 -1

解释

  • 若 dp[amount] 仍为初始值 amount + 1,说明无法兑换,返回 -1

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        if (amount == 0) return 0;
        if (coins.empty()) return -1;

        vector<int> dp(amount + 1, amount + 1);
        dp[0] = 0;   

        for (int coin : coins) {
            for (int j = coin; j <= amount; ++j) {
                dp[j] = min(dp[j], dp[j - coin] + 1);
            }
        }  
        return (dp[amount] > amount)?-1:dp[amount];
    }
};


http://www.kler.cn/a/599367.html

相关文章:

  • 基于linuxC结合epoll + TCP 服务器客户端 + 数据库实现一个注册登录功能
  • 穿越之程序员周树人的狂人日记Part5__硅基驯化录
  • 跨数据库定时数据推送实战
  • 我的世界1.20.1forge模组进阶开发教程——Alex‘s mob的深入研究
  • 蓝桥杯 - 简单 - 布局切换
  • 【时时三省】(C语言基础)选择结构和条件判断
  • 用免费的github的key调用gpt实现一个简单的rag自动打分评测系统,不用任何框架
  • Docker 数据卷管理
  • 模型搭建与复现
  • 同旺科技USB to SPI 适配器 ---- 指令循环发送功能
  • 从指令集鸿沟到硬件抽象:AI 如何重塑手机与电脑编程语言差异——PanLang 原型全栈设计方案与实验性探索1
  • 基于SpringBoot的“社区居民诊疗健康管理系统”的设计与实现(源码+数据库+文档+PPT)
  • tcl语法中的命令
  • word中指定页面开始添加页码
  • 深度拆解:AI Agent发展演练·数字挑战
  • xss-labs
  • C++STL(四):stack和queue的模拟实现
  • python如何提取html中所有的图片链接
  • qt介绍自定义插件 三
  • 为什么后端接口返回数字类型1.00前端会取到1?