返回

LeetCode周赛第343场:激烈角逐,谁是下一位排列之王?

闲谈

第 343 场 LeetCode 单周赛:贪心算法与字符串匹配的精彩对决

简介

欢迎来到第 343 场 LeetCode 单周赛的精彩回顾!在这个紧张刺激的角逐中,参赛者们大显身手,运用贪心构造、字符串匹配、图论、字符串操作和动态规划等算法,解决了一系列富有挑战性的难题。

贪心构造的艺术:下一个排列

第一题“下一个排列”考察了参赛者的贪心构造能力。给定一个数字序列,参赛者需要找到该序列的“下一个排列”,即比当前序列更大的下一个序列,且使用最小的字典序。贪心算法的精髓在于,每一步都选择最优的局部解,最终得到最优的全局解。这道题正是对贪心算法的深刻考验。

代码示例:

def nextPermutation(nums):
    i = len(nums) - 2
    # 从右向左找到第一个降序元素
    while i >= 0 and nums[i] >= nums[i + 1]:
        i -= 1
    # 如果没有降序元素,则序列已经是最后一个排列,反转即可
    if i < 0:
        nums.reverse()
        return

    # 找到大于 nums[i] 的最右侧元素
    j = len(nums) - 1
    while j >= 0 and nums[j] <= nums[i]:
        j -= 1
    
    # 交换 nums[i] 和 nums[j]
    nums[i], nums[j] = nums[j], nums[i]
    
    # 反转 nums[i + 1:]
    nums[i + 1:] = sorted(nums[i + 1:])

字符串匹配的利器:按顺序查找字符

第二题“按顺序查找字符”考察了参赛者的字符串匹配能力。给定一个字符串和一个模式串,参赛者需要找出模式串在字符串中的所有出现位置。滑动窗口算法是解决这道题的有效方法。滑动窗口算法的思路是,将模式串作为一个窗口,在字符串中移动,每移动一步,检查窗口内的字符是否与模式串匹配。如果匹配,则记录下窗口的起始位置。

代码示例:

def findAnagrams(s, p):
    # 记录 p 中各字符出现的次数
    char_count = Counter(p)

    # 记录窗口内各字符出现的次数
    window = Counter()

    # 结果列表
    result = []

    # 遍历字符串 s
    for i in range(len(s)):
        # 更新窗口
        window[s[i]] += 1

        # 窗口大小等于模式串长度时
        if i >= len(p):
            window[s[i - len(p)]] -= 1
            if window[s[i - len(p)]] == 0:
                del window[s[i - len(p)]]

        # 窗口中各字符出现的次数与模式串相同时
        if window == char_count:
            result.append(i - len(p) + 1)

    return result

图论的挑战:最长异或路径

第三题“最长异或路径”考察了参赛者的图论知识。给定一张无向图,参赛者需要找到一条从起点到终点的路径,使得路径上的所有边的异或值的最大。动态规划算法可以有效地解决这道题。动态规划算法的思路是,将问题分解成一系列子问题,然后逐步解决这些子问题,最终得到问题的整体解。

代码示例:

def longestXorPath(graph, start, end):
    # 初始化 dp 表
    dp = [[0 for _ in range(1 << 16)] for _ in range(len(graph))]

    # 设置起点状态
    dp[start][0] = 1

    # 遍历所有节点
    for node in range(len(graph)):
        # 遍历所有异或值
        for xor_value in range(1 << 16):
            # 更新状态
            for neighbor in graph[node]:
                dp[neighbor][xor_value ^ graph[node][neighbor]] = max(dp[neighbor][xor_value ^ graph[node][neighbor]], dp[node][xor_value] + 1)

    # 返回结果
    return dp[end][0]

字符串操作的妙用:重新排列字符串使相邻字符不同

第四题“重新排列字符串使相邻字符不同”考察了参赛者的字符串操作能力。给定一个字符串,参赛者需要将字符串中的字符重新排列,使得相邻的字符不同。哈希表是解决这道题的有效方法。哈希表是一种数据结构,可以快速地查找和插入元素。参赛者可以利用哈希表来记录字符串中每个字符出现的次数,然后根据字符出现的次数来重新排列字符串。

代码示例:

def rearrangeCharacters(s):
    # 记录字符出现的次数
    char_count = Counter(s)

    # 将字符按出现次数降序排序
    char_list = sorted(char_count.items(), key=lambda x: x[1], reverse=True)

    # 重新排列字符串
    result = []
    for char, count in char_list:
        for i in range(count):
            if len(result) == 0 or result[-1] != char:
                result.append(char)
            else:
                # 如果相邻字符相同,则插入一个不同的字符
                for j in range(len(result)):
                    if result[j] != char:
                        result[j], result[-1] = result[-1], result[j]
                        break

    return ''.join(result)

动态规划的巅峰:最长特殊序列 IV

第五题“最长特殊序列 IV”考察了参赛者的动态规划能力。给定一个字符串和一个目标字符串,参赛者需要找到字符串中包含目标字符串的最长特殊序列。特殊序列是指字符串中连续的一段子串,且子串中的每个字符都不同。动态规划算法可以有效地解决这道题。动态规划算法的思路是,将问题分解成一系列子问题,然后逐步解决这些子问题,最终得到问题的整体解。

代码示例:

def longestSpecialSubsequence(s, target):
    # 初始化 dp 表
    dp = [[0 for _ in range(len(s) + 1)] for _ in range(len(target) + 1)]

    # 遍历字符串 s
    for i in range(len(s)):
        # 遍历目标字符串 target
        for j in range(len(target)):
            # 如果当前字符相等,则更新 dp 值
            if s[i] == target[j]:
                dp[j + 1][i + 1] = dp[j][i] + 1
            # 否则,取最大值
            else:
                dp[j + 1][i + 1] = max(dp[j + 1][i], dp[j][i + 1])

    # 返回结果
    return dp[-1][-1]

结论

第 343 场 LeetCode 单周赛再次展现了算法之美。参赛者们运用各种算法,从贪心构造到动态规划,解决了五道具有挑战性的题目。希望这篇文章能帮助大家深入理解这些算法,并激发你们解决更多算法难题的兴趣。

常见问题解答

  1. 什么是贪心算法?

    • 贪心算法是一种分步求解问题的算法,每一步都选择最优的局部解,最终得到全局最优解。
  2. 什么是字符串匹配?

    • 字符串匹配是指在给定的字符串中找到另一个字符串的出现位置。
  3. 什么是滑动窗口算法?

    • 滑动窗口算法是一种字符串匹配算法,将模式串作为一个窗口,在字符串中移动,检查窗口内的字符是否与模式串匹配。
  4. 什么是动态规划算法?

    • 动态规划算法是一种解决优化问题的算法,将问题分解成一系列子问题,然后逐步解决这些子问题,最终得到问题的整体解。
  5. 什么是哈希表?

    • 哈希表是一种数据结构,可以快速地查找和插入元素。哈希表将元素映射到一个键,键的值可以是元素本身或元素的索引。