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

Leetcode求职题目(21)

 1.戳气球

方法一:

class Solution {
    public int[][] rec; // 记忆化数组,记录子问题的结果
    public int[] val;   // 扩展后的数组,前后各加一个1

    public int maxCoins(int[] nums) {
        int n = nums.length;
        val = new int[n + 2]; // 扩展数组,前后各加一个1
        for (int i = 1; i <= n; i++) {
            val[i] = nums[i - 1]; // 复制原始数组
        }
        val[0] = val[n + 1] = 1; // 边界值设为1

        // 初始化记忆化数组,-1表示未计算过
        rec = new int[n + 2][n + 2];
        for (int i = 0; i <= n + 1; i++) {
            Arrays.fill(rec[i], -1);
        }

        // 调用递归函数,计算区间 (0, n+1) 的最大硬币数
        return solve(0, n + 1);
    }

    // 递归函数,计算区间 (left, right) 的最大硬币数
    public int solve(int left, int right) {
        // 如果区间内没有气球,返回0
        if (left >= right - 1) {
            return 0;
        }

        // 如果已经计算过,直接返回结果
        if (rec[left][right] != -1) {
            return rec[left][right];
        }

        // 枚举最后一个被戳破的气球 k
        for (int i = left + 1; i < right; i++) {
            // 计算戳破气球 i 的硬币数
            int sum = val[left] * val[i] * val[right];
            // 递归计算左右两个区间的最大硬币数
            sum += solve(left, i) + solve(i, right);
            // 更新当前区间的最大硬币数
            rec[left][right] = Math.max(rec[left][right], sum);
        }

        // 返回当前区间的最大硬币数
        return rec[left][right];
    }
}

 解题思路

分治法
  • 将问题分解为子问题:假设最后一个被戳破的气球是 k,那么问题可以分解为:

    1. 戳破区间 (left, k) 内的气球。

    2. 戳破区间 (k, right) 内的气球。

    3. 戳破气球 k

  • 通过递归解决子问题,最终合并结果。

 记忆化搜索
  • 使用一个二维数组 rec 记录已经计算过的子问题的结果,避免重复计算。        

代码流程

初始化
  • 创建一个新数组 val,在原始数组前后各添加一个 1,方便处理边界条件。

  • 初始化记忆化数组 rec,所有值设为 -1,表示未计算过。

递归函数 solve
  • 参数left 和 right 表示当前区间的左右边界。

  • 终止条件:如果区间内没有气球(left >= right - 1),返回 0

  • 记忆化检查:如果 rec[left][right] 已经计算过,直接返回结果。

  • 枚举最后一个戳破的气球

    • 遍历区间 (left + 1, right - 1),假设最后一个戳破的气球是 i

    • 计算戳破气球 i 的硬币数:val[left] * val[i] * val[right]

    • 递归计算左右两个区间的最大硬币数:solve(left, i) 和 solve(i, right)

    • 更新当前区间的最大硬币数:rec[left][right] = max(rec[left][right], sum)

  • 返回结果:返回 rec[left][right]

另一种方法:

class Solution {
    public int maxCoins(int[] nums) {
        // 创建新数组,首尾添加1
        int n = nums.length;
        int[] points = new int[n + 2];
        points[0] = 1;
        points[n + 1] = 1;
        for (int i = 0; i < n; i++) {
            points[i + 1] = nums[i];
        }
        
        // 创建dp数组
        int[][] dp = new int[n + 2][n + 2];
        
        // 开始dp:len表示开区间长度
        for (int len = 1; len <= n; len++) {
            // i表示开区间左端点
            for (int i = 0; i <= n - len; i++) {
                int j = i + len + 1; // 开区间右端点
                // k为最后一个被戳破的气球
                for (int k = i + 1; k < j; k++) {
                    // 状态转移方程
                    dp[i][j] = Math.max(
                        dp[i][j],
                        dp[i][k] + dp[k][j] + points[i] * points[k] * points[j]
                    );
                }
            }
        }
        
        // 返回整个区间的最大值
        return dp[0][n + 1];
    }
}

 解题思路:

 2.去除重复字母

这道题是2024年华为面试题

 

关键策略
  1. 贪心选择:尽可能选择更小的字符作为前导

  2. 延迟删除:当发现更优选择时,只有确定后续还能找到当前字符时才删除前面的字符

逐步解析
  1. 预处理阶段

    • 使用lastOccurrence数组记录每个字符最后一次出现的索引位置

    • 示例:对于"bcabc"

      • b最后出现位置:1

      • c最后出现位置:4

      • a最后出现位置:2

  2. 主处理阶段

    • 遍历每个字符时进行以下判断:

    • 跳过条件:当前字符已经在结果栈中(保证唯一性)

    • 优化条件(同时满足时执行栈顶弹出):

      1. 当前字符 < 栈顶字符(有机会获得更小字典序)

      2. 栈顶字符后续还会出现(删除后有机会重新获取)

  3. 栈维护

    • 始终保证栈中字符保持递增顺序(除非后续没有机会再调整)

    • 每次添加新字符时更新使用标记

 

class Solution {
    public String removeDuplicateLetters(String s) {
        // 记录每个字符最后一次出现的位置
        int[] lastOccurrence = new int[26];
        // 标记字符是否已存在于结果栈中
        boolean[] used = new boolean[26];
        // 用StringBuilder模拟栈结构
        StringBuilder stack = new StringBuilder();

        // Step 1: 预处理每个字符的最后出现位置
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            lastOccurrence[c - 'a'] = i; // 记录字符c的最后位置
        }

        // Step 2: 遍历字符串构建结果
        for (int i = 0; i < s.length(); i++) {
            char currentChar = s.charAt(i);
            int charIndex = currentChar - 'a';

            // 如果当前字符已经在栈中,跳过
            if (used[charIndex]) continue;

            // Step 3: 贪心移除可以优化的前导字符
            while (stack.length() > 0 
                    && currentChar < stack.charAt(stack.length()-1) // 当前字符更小
                    && lastOccurrence[stack.charAt(stack.length()-1)-'a'] > i) { // 栈顶字符后续还会出现
                // 移除栈顶字符并更新使用标记
                char removedChar = stack.charAt(stack.length()-1);
                used[removedChar - 'a'] = false;
                stack.deleteCharAt(stack.length()-1);
            }

            // Step 4: 将当前字符入栈
            stack.append(currentChar);
            used[charIndex] = true;
        }

        return stack.toString();
    }
}

3.最大单词长度乘积

解题思路:

1. 预处理阶段(时间复杂度 O(n*L),L为单词平均长度)
  • 目标:将每个单词的字符存在性快速记录下来

  • 实现

    • 使用 boolean[n][26] 数组,chars[i][k] = true 表示第i个单词包含字符 (char)('a' + k)

    • 遍历每个单词的每个字符,填充布尔数组

 

计算阶段(时间复杂度 O(n²*26))
  • 目标:检查所有单词对,找出无公共字符的最大乘积

  • 实现

    • 双重循环遍历所有单词对 (i, j)(i < j)

    • 内层循环检查两单词是否有公共字符:

      • 若存在任意一个字符同时存在于两个单词中,标记为有公共字符

    • 若无公共字符,计算两单词长度的乘积,更新最大值

    class Solution {
        public int maxProduct(String[] words) {
            int maxProduct = 0;
            int n = words.length;
            boolean[][] chars = new boolean[n][26];  // 记录每个单词包含的字符
            
            // 预处理所有单词的字符组成
            for (int i = 0; i < n; i++) {
                for (char c : words[i].toCharArray()) {
                    chars[i][c - 'a'] = true;
                }
            }
            
            // 检查每对单词
            for (int i = 0; i < n; i++) {
                for (int j = i + 1; j < n; j++) {
                    boolean hasCommon = false;
                    // 检查是否有公共字符
                    for (int k = 0; k < 26; k++) {
                        if (chars[i][k] && chars[j][k]) {
                            hasCommon = true;
                            break;
                        }
                    }
                    
                    if (!hasCommon) {
                        maxProduct = Math.max(maxProduct, 
                            words[i].length() * words[j].length());
                    }
                }
            }
            
            return maxProduct;
        }
    }


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

相关文章:

  • 适合 C# 开发者的 Semantic Kernel 入门:用 AI 赋能你的 .NET 应用
  • 【由浅入深认识Maven】第1部分 maven简介与核心概念
  • 回溯算法学习记录及习题集合
  • JavaScript常见面试问题解答
  • 代码随想录训练营第五十六天| 108.冗余连接 109.冗余连接II
  • 2024年蓝桥杯真题C/C++/java组部分真题解析(一)
  • 手撕Diffusion系列 - 第九期 - 改进为Stable Diffusion(原理介绍)
  • mysql create table的用法
  • INCOSE需求编写指南-第 2 节:需求和要求陈述的特征
  • PD协议(Power Delivery)高效安全解决充电宝给笔记本供电
  • Android BitmapShader简洁实现马赛克/高斯模糊(毛玻璃),Kotlin(三)
  • javascript格式化对象数组:ES6的模板字符串、map
  • 深度学习|表示学习|卷积神经网络|Pooling(池化是在做什么)|13
  • 通过循环添加组件
  • 消息队列篇--通信协议篇--TCP和UDP(3次握手和4次挥手,与Socket和webSocket的概念区别等)
  • Maui学习笔记-身份认证和授权案例
  • MAX98357A一款数字脉冲编码调制(PCM)输入D类音频功率放大器
  • RACER:基于去中心化多无人机系统的快速协同探索
  • Alibaba Spring Cloud 十三 Nacos,Gateway,Nginx 部署架构与负载均衡方案
  • AI导航工具我开源了利用node爬取了几百条数据