普通视图

发现新文章,点击刷新页面。
今天 — 2026年4月23日LeetCode 每日一题题解

每日一题-等值距离和🟡

2026年4月23日 00:00

给你一个下标从 0 开始的整数数组 nums 。现有一个长度等于 nums.length 的数组 arr 。对于满足 nums[j] == nums[i]j != i 的所有 jarr[i] 等于所有 |i - j| 之和。如果不存在这样的 j ,则令 arr[i] 等于 0

返回数组 arr

 

示例 1:

输入:nums = [1,3,1,1,2]
输出:[5,0,3,4,0]
解释:
i = 0 ,nums[0] == nums[2] 且 nums[0] == nums[3] 。因此,arr[0] = |0 - 2| + |0 - 3| = 5 。 
i = 1 ,arr[1] = 0 因为不存在值等于 3 的其他下标。
i = 2 ,nums[2] == nums[0] 且 nums[2] == nums[3] 。因此,arr[2] = |2 - 0| + |2 - 3| = 3 。
i = 3 ,nums[3] == nums[0] 且 nums[3] == nums[2] 。因此,arr[3] = |3 - 0| + |3 - 2| = 4 。 
i = 4 ,arr[4] = 0 因为不存在值等于 2 的其他下标。

示例 2:

输入:nums = [0,5,3]
输出:[0,0,0]
解释:因为 nums 中的元素互不相同,对于所有 i ,都有 arr[i] = 0 。

 

提示:

  • 1 <= nums.length <= 105
  • 0 <= nums[i] <= 109

小白讲给小白的前缀和

作者 sevenoutman
2023年4月9日 13:13

Problem: 6360. 等值距离和

[TOC]

思路

因为肯定要找出 nums[i]=nums[j] 的所有 i,j,所以首先遍历一遍,记录各个数值出现的每个下标。拿到了各个数值的下标数组后,对数组中的各个元素,求它与其他元素之间的距离和。

关键在于,如何快速地求出数组中元素与其他元素的距离和?在我不知道“前缀和”的概念及其运用方法之前,我是直接遍历求和的,由于题目说 nums.length <= 105, 不出意外地超时了。随后我从题目提示中得知了“前缀和”的概念,并从其他题解中了解了前缀和在本题中的运用,故写此题解给像我一样的小白讲解前缀和与本题的关系。

解题方法

前缀和

参考 https://www.cnblogs.com/ZghzzZyu/p/16407303.html

前缀和指的是,对于数组 nums, 有前缀和数组 arr, 其每一项 arr[i] 的值为 nums[0] + nums[1] + ... + nums[i] 的和。

$arr[i] = sum(nums[0...i])$

例如有数组 nums 如下:

$nums = [0, 1, 2, 3, 4]$

则对应的前缀和数组为:

$arr = [0, 1, 3, 6, 10]$

根据定义不难看出,前缀和数组的每一项,都为其前一项加上 nums 中当前的项。

$arr[i] = arr[i - 1] + nums[i]$

这使得我们在遍历 nums 的每一项时,可以立即计算出 arr 中对应的项,这也是利用前缀和解这道题时降低时间复杂度的关键。在思路中讲到,我们首先遍历 nums 并记录每个数字出现的下标数组,现在,我们改为在遍历 nums 时记录每个数字出现的下标数组的前缀和数组。

通过前缀和求距离和

参考 https://leetcode.cn/problems/sum-of-distances/solutions/2216328/an-shu-zi-xia-biao-qian-zhui-he-by-yusha-xzu4/

有了前缀和数组,就能很方便地求出距离和了。例如,如果我们拥有数组 nums 如下:

$nums = [0, 1, 2, 3, 4]$

当我们要求元素 $2$ 到其他元素的距离和时,计算过程如下:

对于小于 2 的元素 n,计算 $2 - n$ 并相加。不难发现,其结果为小于 2 的元素总和(即前缀和),减去 2 乘以小于 2 的元素数量

$(2 - 0) + (2 - 1) = 2*2 - (0 + 1) = 2 * count(0...1) - sum(0...1)$

对于大于 2 的元素 n,计算 $n - 2$ 并相加。不难发现,其结果为大于 2 的元素总和,减去 2 乘以大于 2 的元素数量

$(3 - 2) + (4 - 2) = (3 + 4) - 2 * 2 = sum(3...4) - 2 * count(3...4)$

根据前缀和的特性,$sum(3...4)$ 可以通过 $sum(0...4)$ 减去 $sum(0...2)$ 得出。

$sum(3...4) = sum(0...4) - sum(0...2)$

元素 2 到其他元素的距离和即为上面两部分相加。

Code

###TypeScript

function distance(nums: number[]): number[] {
  const answer = new Array<number>(nums.length).fill(0)
  // 用于记录各个数值出现的下标数组的前缀和数组
  const map = new Map<number, number[]>();

  for (let i = 0; i < nums.length; i++) {
    if (!map.has(nums[i])) {
      map.set(nums[i], [i])
    } else {
      const array = map.get(nums[i])
      // 计算前缀和
      array.push(array[array.length - 1] + i)
    }
  }

  // 用于记录当前下标是下标数组中的第几个元素
  const indexCounter = []
  for (let i = 0; i < nums.length; i++) {
    const array = map.get(nums[i])

    if (array.length > 1) {
      const indexOfIndex = typeof indexCounter[nums[i]] === 'undefined' ? (indexCounter[nums[i]] = 0) : (indexCounter[nums[i]])
      indexCounter[nums[i]]++

      // 套上面的计算公式
      answer[i] =
        (i * indexOfIndex - (array[indexOfIndex - 1] ?? 0)) +
        (array[array.length - 1] - array[indexOfIndex] - i * (array.length - 1 - indexOfIndex))
    }
  }

  return answer
};

两种 O(n) 方法:照搬 2602 / 考虑距离之和的增量

作者 endlesscheng
2023年4月9日 12:31

本题视频讲解

【周赛 340】

方法一:相同元素分组+前缀和

按照相同元素分组后再计算。

这里的思路和 2602. 使数组元素全部相等的最少操作次数(题解)是一样的。由于目标位置就是数组中的下标,无需二分。

class Solution:
    def distance(self, nums: List[int]) -> List[int]:
        groups = defaultdict(list)
        for i, x in enumerate(nums):
            groups[x].append(i)  # 相同元素分到同一组,记录下标
        ans = [0] * len(nums)
        for a in groups.values():
            n = len(a)
            s = list(accumulate(a, initial=0))  # 前缀和
            for j, target in enumerate(a):
                left = target * j - s[j]  # 蓝色面积
                right = s[n] - s[j] - target * (n - j)  # 绿色面积
                ans[target] = left + right
        return ans
class Solution {
    public long[] distance(int[] nums) {
        int n = nums.length;
        var groups = new HashMap<Integer, List<Integer>>();
        for (int i = 0; i < n; ++i) // 相同元素分到同一组,记录下标
            groups.computeIfAbsent(nums[i], k -> new ArrayList<>()).add(i);

        var ans = new long[n];
        var s = new long[n + 1];
        for (var a : groups.values()) {
            int m = a.size();
            for (int i = 0; i < m; ++i)
                s[i + 1] = s[i] + a.get(i); // 前缀和
            for (int i = 0; i < m; ++i) {
                int target = a.get(i);
                long left = (long) target * i - s[i]; // 蓝色面积
                long right = s[m] - s[i] - (long) target * (m - i); // 绿色面积
                ans[target] = left + right;
            }
        }
        return ans;
    }
}
class Solution {
public:
    vector<long long> distance(vector<int>& nums) {
        int n = nums.size();
        unordered_map<int, vector<int>> groups;
        for (int i = 0; i < n; ++i)
            groups[nums[i]].push_back(i); // 相同元素分到同一组,记录下标

        vector<long long> ans(n);
        long long s[n + 1];
        s[0] = 0;
        for (auto& [_, a]: groups) {
            int m = a.size();
            for (int i = 0; i < m; ++i)
                s[i + 1] = s[i] + a[i]; // 前缀和
            for (int i = 0; i < m; ++i) {
                long long target = a[i];
                long long left = target * i - s[i]; // 蓝色面积
                long long right = s[m] - s[i] - target * (m - i); // 绿色面积
                ans[target] = left + right;
            }
        }
        return ans;
    }
};
func distance(nums []int) []int64 {
groups := map[int][]int{}
for i, x := range nums {
groups[x] = append(groups[x], i) // 相同元素分到同一组,记录下标
}
ans := make([]int64, len(nums))
for _, a := range groups {
n := len(a)
s := make([]int, n+1)
for i, x := range a {
s[i+1] = s[i] + x // 前缀和
}
for i, target := range a {
left := target*i - s[i] // 蓝色面积
right := s[n] - s[i] - target*(n-i) // 绿色面积
ans[target] = int64(left + right)
}
}
return ans
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(n)$,其中 $n$ 为 $\textit{nums}$ 的长度。
  • 空间复杂度:$\mathcal{O}(n)$。

方法二:相同元素分组+考虑增量

分组后,对于其中一个组 $a$,我们先暴力计算出 $a[0]$ 到其它元素的距离之和,设为 $s_0$。

然后计算 $a[1]$ 到其它元素的距离之和 $s_1$。我们不再暴力计算,而是思考:从 $s_0$ 到 $s_1$,这个增量 $s_1-s_0$ 是多少?(增量可以是负数)

设 $n$ 为 $a$ 的长度,从 $s_0$ 到 $s_1$,有 $1$ 个数的距离变大了 $a[1]-a[0]$(原来是到 $a[0]$ 的距离,现在是到 $a[1]$ 的距离;原来是 $a[0]$ 到 $a[0]$ 的距离为 $0$,现在是 $a[0]$ 到 $a[1]$ 的距离为 $a[1]-a[0]$),其余 $n-1$ 个数的距离变小了 $a[1]-a[0]$(原来是到 $a[0]$ 的距离,现在是到 $a[1]$ 的距离)。

所以对于 $s_1$,我们可以 $\mathcal{O}(1)$ 地知道 $a[1]$ 到其它元素的距离之和为

$$
s_0 + (2-n) \cdot (a[1]-a[0])
$$

一般地,设 $a[i-1]$ 到其它元素的距离之和为 $s$,那么 $a[i]$ 到其它元素的距离之和为

$$
s + (2i-n) \cdot (a[i]-a[i-1])
$$

class Solution:
    def distance(self, nums: List[int]) -> List[int]:
        groups = defaultdict(list)
        for i, x in enumerate(nums):
            groups[x].append(i)  # 相同元素分到同一组,记录下标
        ans = [0] * len(nums)
        for a in groups.values():
            n = len(a)
            s = sum(x - a[0] for x in a)  # a[0] 到其它下标的距离之和
            ans[a[0]] = s
            for i in range(1, n):
                # 从计算 a[i-1] 到计算 a[i],考虑 s 增加了多少
                s += (i * 2 - n) * (a[i] - a[i - 1])
                ans[a[i]] = s
        return ans
class Solution {
    public long[] distance(int[] nums) {
        int n = nums.length;
        var groups = new HashMap<Integer, List<Integer>>();
        for (int i = 0; i < n; ++i) // 相同元素分到同一组,记录下标
            groups.computeIfAbsent(nums[i], k -> new ArrayList<>()).add(i);

        var ans = new long[n];
        for (var a : groups.values()) {
            int m = a.size();
            long s = 0;
            for (int x : a)
                s += x - a.get(0); // a[0] 到其它下标的距离之和
            ans[a.get(0)] = s;
            for (int i = 1; i < m; ++i)
                // 从计算 a[i-1] 到计算 a[i],考虑 s 增加了多少
                ans[a.get(i)] = s += (long) (i * 2 - m) * (a.get(i) - a.get(i - 1));
        }
        return ans;
    }
}
class Solution {
public:
    vector<long long> distance(vector<int>& nums) {
        int n = nums.size();
        unordered_map<int, vector<int>> groups;
        for (int i = 0; i < n; ++i)
            groups[nums[i]].push_back(i); // 相同元素分到同一组,记录下标

        vector<long long> ans(n);
        for (auto& [_, a]: groups) {
            int m = a.size();
            long long s = 0;
            for (int x: a)
                s += x - a[0]; // a[0] 到其它下标的距离之和
            ans[a[0]] = s;
            for (int i = 1; i < m; ++i)
                // 从计算 a[i-1] 到计算 a[i],考虑 s 增加了多少
                ans[a[i]] = s += (long long) (i * 2 - m) * (a[i] - a[i - 1]);
        }
        return ans;
    }
};
func distance(nums []int) []int64 {
groups := map[int][]int{}
for i, x := range nums {
groups[x] = append(groups[x], i) // 相同元素分到同一组,记录下标
}
ans := make([]int64, len(nums))
for _, a := range groups {
n := len(a)
s := int64(0)
for _, x := range a {
s += int64(x - a[0]) // a[0] 到其它下标的距离之和
}
ans[a[0]] = s
for i := 1; i < n; i++ {
// 从计算 a[i-1] 到计算 a[i],考虑 s 增加了多少
s += int64(i*2-n) * int64(a[i]-a[i-1])
ans[a[i]] = s
}
}
return ans
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(n)$,其中 $n$ 为 $\textit{nums}$ 的长度。
  • 空间复杂度:$\mathcal{O}(n)$。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、二叉树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA/一般树)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

前缀和

作者 tsreaper
2023年4月9日 12:28

解法:前缀和

注意到如果 nums[i]nums[j] 不同,则它们不会互相影响。因此我们可以用一个 unordered_map<int, vector<int>> 保存同一种数的所有下标,然后单独处理每种数即可。

接下来考虑某一种数的答案怎么算出来。问题其实就是:给一个有序数组,求每个元素和其它元素差值的绝对值的和。这是出现过无数次的经典问题,解法参考 leetcode 1685. 有序数组中差绝对值之和

复杂度 $\mathcal{O}(n)$。

参考代码(c++)

###c++

class Solution {
public:
    vector<long long> distance(vector<int>& nums) {
        int n = nums.size();

        // 维护每种数的所有下标
        unordered_map<int, vector<int>> mp;
        for (int i = 0; i < n; i++) mp[nums[i]].push_back(i);

        vector<long long> ans(n);
        // 求某一种数的答案
        auto gao = [&](vector<int> &vec) {
            int n = vec.size();
            long long f[n + 1];
            f[0] = 0;
            for (int i = 0; i < n; i++) f[i + 1] = f[i] + vec[i];
            for (int i = 1; i <= n; i++) {
                long long L = 1LL * i * vec[i - 1] - f[i];
                long long R = (f[n] - f[i]) - 1LL * (n - i) * vec[i - 1];
                ans[vec[i - 1]] = L + R;
            }
        };

        // 对每种数求答案
        for (auto &p : mp) gao(p.second);
        return ans;
    }
};
昨天 — 2026年4月22日LeetCode 每日一题题解

距离字典两次编辑以内的单词

2026年4月13日 10:21

方法一:暴力

思路与算法

注意到题目的数据范围很小,我们可以直接实施暴力算法。

对于 $\textit{queries}$ 中的每个字符串 $\textit{queries}[i]$,查找 $\textit{dictionary}$ 中是否存在一个字符串,使得两个字符串中最多只有两个字符不同(即汉明距离小于等于 $2$),如果存在,就将其添加到答案中。由于添加的顺序与遍历 $\textit{queries}$ 的顺序一致,我们不需要对答案的顺序进行特殊处理。

代码

###C++

class Solution {
public:
    vector<string> twoEditWords(vector<string>& queries,
                                vector<string>& dictionary) {
        vector<string> ans;
        for (string query : queries) {
            for (string s : dictionary) {
                int dis = 0;
                for (int i = 0; i < query.size(); i++) {
                    if (query[i] != s[i]) {
                        ++dis;
                    }
                }
                if (dis <= 2) {
                    ans.push_back(query);
                    break;
                }
            }
        }
        return ans;
    }
};

###Java

class Solution {
    public List<String> twoEditWords(String[] queries, String[] dictionary) {
        List<String> ans = new ArrayList<>();
        for (String query : queries) {
            for (String s : dictionary) {
                int dis = 0;
                for (int i = 0; i < query.length(); i++) {
                    if (query.charAt(i) != s.charAt(i)) {
                        dis++;
                    }
                }
                if (dis <= 2) {
                    ans.add(query);
                    break;
                }
            }
        }
        return ans;
    }
}

###C#

public class Solution {
    public IList<string> TwoEditWords(string[] queries, string[] dictionary) {
        var ans = new List<string>();
        foreach (var query in queries) {
            foreach (var s in dictionary) {
                int dis = 0;
                for (int i = 0; i < query.Length; i++) {
                    if (query[i] != s[i]) {
                        dis++;
                    }
                }
                if (dis <= 2) {
                    ans.Add(query);
                    break;
                }
            }
        }
        return ans;
    }
}

###Python

class Solution:
    def twoEditWords(self, queries, dictionary):
        ans = []
        for query in queries:
            for s in dictionary:
                dis = 0
                for i in range(len(query)):
                    if query[i] != s[i]:
                        dis += 1
                if dis <= 2:
                    ans.append(query)
                    break
        return ans

###C

char** twoEditWords(char** queries, int queriesSize,
                    char** dictionary, int dictionarySize,
                    int* returnSize) {
    char** ans = (char**)malloc(sizeof(char*) * queriesSize);
    int cnt = 0;

    for (int i = 0; i < queriesSize; i++) {
        char* query = queries[i];
        for (int j = 0; j < dictionarySize; j++) {
            char* s = dictionary[j];
            int dis = 0;
            for (int k = 0; query[k] != '\0'; k++) {
                if (query[k] != s[k]) {
                    dis++;
                }
            }
            if (dis <= 2) {
                ans[cnt++] = query;
                break;
            }
        }
    }

    *returnSize = cnt;
    return ans;
}

###Go

func twoEditWords(queries []string, dictionary []string) []string {
    var ans []string
    for _, query := range queries {
        for _, s := range dictionary {
            dis := 0
            for i := 0; i < len(query); i++ {
                if query[i] != s[i] {
                    dis++
                }
            }
            if dis <= 2 {
                ans = append(ans, query)
                break
            }
        }
    }
    return ans
}

###JavaScript

var twoEditWords = function(queries, dictionary) {
    const ans = [];
    for (const query of queries) {
        for (const s of dictionary) {
            let dis = 0;
            for (let i = 0; i < query.length; i++) {
                if (query[i] !== s[i]) {
                    dis++;
                }
            }
            if (dis <= 2) {
                ans.push(query);
                break;
            }
        }
    }
    return ans;
};

###TypeScript

function twoEditWords(queries: string[], dictionary: string[]): string[] {
    const ans: string[] = [];
    for (const query of queries) {
        for (const s of dictionary) {
            let dis = 0;
            for (let i = 0; i < query.length; i++) {
                if (query[i] !== s[i]) {
                    dis++;
                }
            }
            if (dis <= 2) {
                ans.push(query);
                break;
            }
        }
    }
    return ans;
}

###Rust

impl Solution {
    pub fn two_edit_words(queries: Vec<String>, dictionary: Vec<String>) -> Vec<String> {
        let mut ans = Vec::new();

        for query in &queries {
            for s in &dictionary {
                let mut dis = 0;
                for (c1, c2) in query.chars().zip(s.chars()) {
                    if c1 != c2 {
                        dis += 1;
                    }
                }
                if dis <= 2 {
                    ans.push(query.clone());
                    break;
                }
            }
        }

        ans
    }
}

复杂度分析

  • 时间复杂度:$O(qkn)$,其中 $q$ 为 $\textit{queries}$ 的长度,$k$ 为 $\textit{dictionary}$ 的长度,$n$ 为 $\textit{queries}[i]$ 的长度。我们需要对每一个 $\textit{queries}[i]$ 遍历一次 $\textit{dictionary}$,然后比较两个字符串。
  • 空间复杂度:$O(1)$,仅使用常数个变量。返回数组不计入空间复杂度。

方法二:字典树

思路与算法

我们可以将 $\textit{dictionary}$ 中的所有单词插入字典树,对每个 $\textit{queries}[i]$ 做深度优先搜索,在过程中维护修改次数 $\textit{cnt}$,从而实现在字典树上进行「最多 2 次修改」的匹配搜索。

定义状态 $\textit{dfs}(i, \textit{node}, \textit{cnt})$,其中 $i$ 表示当前匹配到第 $i$ 个字符,$\textit{node}$ 表示当前所在字典树的节点,$\textit{cnt}$ 表示已修改 $\textit{cnt}$ 次。在字典树上进行查找,对于第 $i$ 个字符 $\textit{query}[i]$:

  1. 如果字典树中存在 $\textit{node}.\textit{children}[\textit{query}[i]]$,不进行修改,下一步搜索状态 $\textit{dfs}(i+1, \textit{node}.\textit{children}[\textit{query}[i]], \textit{cnt})$。
  2. 如果字典树中不存在 $\textit{node}.\textit{children}[\textit{query}[i]]$,且 $\textit{cnt}<2$,进行修改,即枚举所有 $c \neq \textit{query}[i]$,下一步搜索状态 $\textit{dfs}(i+1,\textit{node}.\textit{children}[c], \textit{cnt}+1)$。

搜索过程中,我们可以进行一些剪枝,比如某条路径找到合法解就提前终止。

代码

###C++

struct TrieNode {
    TrieNode* child[26];
    bool isEnd;
    TrieNode() {
        memset(child, 0, sizeof(child));
        isEnd = false;
    }
};

class Solution {
public:
    TrieNode* root = new TrieNode();

    void insert(string& word) {
        TrieNode* node = root;
        for (char c : word) {
            int idx = c - 'a';
            if (!node->child[idx])
                node->child[idx] = new TrieNode();
            node = node->child[idx];
        }
        node->isEnd = true;
    }

    bool dfs(string& word, int i, TrieNode* node, int cnt) {
        if (cnt > 2)
            return false;
        if (!node)
            return false;

        if (i == word.size()) {
            return node->isEnd;
        }

        int idx = word[i] - 'a';

        // 不修改
        if (node->child[idx]) {
            if (dfs(word, i + 1, node->child[idx], cnt))
                return true;
        }

        // 修改
        if (cnt < 2) {
            for (int c = 0; c < 26; c++) {
                if (c == idx)
                    continue;
                if (node->child[c]) {
                    if (dfs(word, i + 1, node->child[c], cnt + 1))
                        return true;
                }
            }
        }

        return false;
    }

    vector<string> twoEditWords(vector<string>& queries,
                                vector<string>& dictionary) {
        for (auto& w : dictionary)
            insert(w);

        vector<string> res;
        for (auto& q : queries) {
            if (dfs(q, 0, root, 0)) {
                res.push_back(q);
            }
        }
        return res;
    }
};

###Java

class Solution {
    static class TrieNode {
        TrieNode[] child = new TrieNode[26];
        boolean isEnd = false;
    }

    TrieNode root = new TrieNode();

    void insert(String word) {
        TrieNode node = root;
        for (char c : word.toCharArray()) {
            int idx = c - 'a';
            if (node.child[idx] == null)
                node.child[idx] = new TrieNode();
            node = node.child[idx];
        }
        node.isEnd = true;
    }

    boolean dfs(String word, int i, TrieNode node, int cnt) {
        if (cnt > 2 || node == null)
            return false;

        if (i == word.length())
            return node.isEnd;

        int idx = word.charAt(i) - 'a';

        // 不修改
        if (node.child[idx] != null) {
            if (dfs(word, i + 1, node.child[idx], cnt))
                return true;
        }

        // 修改
        if (cnt < 2) {
            for (int c = 0; c < 26; c++) {
                if (c == idx)
                    continue;
                if (node.child[c] != null) {
                    if (dfs(word, i + 1, node.child[c], cnt + 1))
                        return true;
                }
            }
        }

        return false;
    }

    public List<String> twoEditWords(String[] queries, String[] dictionary) {
        for (String w : dictionary)
            insert(w);

        List<String> res = new ArrayList<>();
        for (String q : queries) {
            if (dfs(q, 0, root, 0)) {
                res.add(q);
            }
        }
        return res;
    }
}

###C#

public class Solution {
    class TrieNode {
        public TrieNode[] child = new TrieNode[26];
        public bool isEnd = false;
    }

    TrieNode root = new TrieNode();

    void Insert(string word) {
        var node = root;
        foreach (char c in word) {
            int idx = c - 'a';
            if (node.child[idx] == null)
                node.child[idx] = new TrieNode();
            node = node.child[idx];
        }
        node.isEnd = true;
    }

    bool Dfs(string word, int i, TrieNode node, int cnt) {
        if (cnt > 2 || node == null)
            return false;

        if (i == word.Length)
            return node.isEnd;

        int idx = word[i] - 'a';

        // 不修改
        if (node.child[idx] != null) {
            if (Dfs(word, i + 1, node.child[idx], cnt))
                return true;
        }

        // 修改
        if (cnt < 2) {
            for (int c = 0; c < 26; c++) {
                if (c == idx) continue;
                if (node.child[c] != null) {
                    if (Dfs(word, i + 1, node.child[c], cnt + 1))
                        return true;
                }
            }
        }

        return false;
    }

    public IList<string> TwoEditWords(string[] queries, string[] dictionary) {
        foreach (var w in dictionary)
            Insert(w);

        var res = new List<string>();
        foreach (var q in queries) {
            if (Dfs(q, 0, root, 0)) {
                res.Add(q);
            }
        }
        return res;
    }
}

###Python

class TrieNode:
    def __init__(self):
        self.child = [None] * 26
        self.isEnd = False


class Solution:
    def __init__(self):
        self.root = TrieNode()

    def insert(self, word):
        node = self.root
        for c in word:
            idx = ord(c) - ord('a')
            if not node.child[idx]:
                node.child[idx] = TrieNode()
            node = node.child[idx]
        node.isEnd = True

    def dfs(self, word, i, node, cnt):
        if cnt > 2 or not node:
            return False

        if i == len(word):
            return node.isEnd

        idx = ord(word[i]) - ord('a')

        # 不修改
        if node.child[idx] and self.dfs(word, i + 1, node.child[idx], cnt):
            return True

        # 修改
        if cnt < 2:
            for c in range(26):
                if c == idx:
                    continue
                if node.child[c] and self.dfs(word, i + 1, node.child[c], cnt + 1):
                    return True

        return False

    def twoEditWords(self, queries, dictionary):
        for w in dictionary:
            self.insert(w)

        res = []
        for q in queries:
            if self.dfs(q, 0, self.root, 0):
                res.append(q)
        return res

###C

typedef struct TrieNode {
    struct TrieNode* child[26];
    bool isEnd;
} TrieNode;

TrieNode* newNode() {
    TrieNode* node = (TrieNode*)malloc(sizeof(TrieNode));
    memset(node->child, 0, sizeof(node->child));
    node->isEnd = false;
    return node;
}

void insert(TrieNode* root, char* word) {
    TrieNode* node = root;
    for (int i = 0; word[i]; i++) {
        int idx = word[i] - 'a';
        if (!node->child[idx])
            node->child[idx] = newNode();
        node = node->child[idx];
    }
    node->isEnd = true;
}

bool dfs(char* word, int i, TrieNode* node, int cnt) {
    if (cnt > 2 || !node)
        return false;

    if (word[i] == '\0')
        return node->isEnd;

    int idx = word[i] - 'a';

    // 不修改
    if (node->child[idx] && dfs(word, i + 1, node->child[idx], cnt))
        return true;

    // 修改
    if (cnt < 2) {
        for (int c = 0; c < 26; c++) {
            if (c == idx) continue;
            if (node->child[c] && dfs(word, i + 1, node->child[c], cnt + 1))
                return true;
        }
    }

    return false;
}

char** twoEditWords(char** queries, int queriesSize,
                    char** dictionary, int dictionarySize,
                    int* returnSize) {
    TrieNode* root = newNode();
    for (int i = 0; i < dictionarySize; i++)
        insert(root, dictionary[i]);

    char** res = (char**)malloc(sizeof(char*) * queriesSize);
    int cnt = 0;

    for (int i = 0; i < queriesSize; i++) {
        if (dfs(queries[i], 0, root, 0)) {
            res[cnt++] = queries[i];
        }
    }

    *returnSize = cnt;
    return res;
}

###Go

type TrieNode struct {
    child [26]*TrieNode
    isEnd bool
}

var root = &TrieNode{}

func insert(word string) {
    node := root
    for _, c := range word {
        idx := c - 'a'
        if node.child[idx] == nil {
            node.child[idx] = &TrieNode{}
        }
        node = node.child[idx]
    }
    node.isEnd = true
}

func dfs(word string, i int, node *TrieNode, cnt int) bool {
    if cnt > 2 || node == nil {
        return false
    }

    if i == len(word) {
        return node.isEnd
    }

    idx := word[i] - 'a'

    // 不修改
    if node.child[idx] != nil && dfs(word, i+1, node.child[idx], cnt) {
        return true
    }

    // 修改
    if cnt < 2 {
        for c := 0; c < 26; c++ {
            if byte(c) == idx {
                continue
            }
            if node.child[c] != nil && dfs(word, i+1, node.child[c], cnt+1) {
                return true
            }
        }
    }

    return false
}

func twoEditWords(queries []string, dictionary []string) []string {
    root = &TrieNode{}
    for _, w := range dictionary {
        insert(w)
    }

    var res []string
    for _, q := range queries {
        if dfs(q, 0, root, 0) {
            res = append(res, q)
        }
    }
    return res
}

###JavaScript

class TrieNode {
    constructor() {
        this.child = new Array(26).fill(null);
        this.isEnd = false;
    }
}

var twoEditWords = function(queries, dictionary) {
    const root = new TrieNode();

    function insert(word) {
        let node = root;
        for (let c of word) {
            let idx = c.charCodeAt(0) - 97;
            if (!node.child[idx]) node.child[idx] = new TrieNode();
            node = node.child[idx];
        }
        node.isEnd = true;
    }

    function dfs(word, i, node, cnt) {
        if (cnt > 2 || !node) return false;
        if (i === word.length) return node.isEnd;

        let idx = word.charCodeAt(i) - 97;

        // 修改
        if (node.child[idx] && dfs(word, i + 1, node.child[idx], cnt))
            return true;

        // 不修改
        if (cnt < 2) {
            for (let c = 0; c < 26; c++) {
                if (c === idx) continue;
                if (node.child[c] && dfs(word, i + 1, node.child[c], cnt + 1))
                    return true;
            }
        }

        return false;
    }

    for (let w of dictionary) insert(w);

    const res = [];
    for (let q of queries) {
        if (dfs(q, 0, root, 0)) res.push(q);
    }

    return res;
};

###TypeScript

class TrieNode {
    child: (TrieNode | null)[] = new Array(26).fill(null);
    isEnd: boolean = false;
}

function twoEditWords(queries: string[], dictionary: string[]): string[] {
    const root = new TrieNode();

    function insert(word: string) {
        let node = root;
        for (const c of word) {
            const idx = c.charCodeAt(0) - 97;
            if (!node.child[idx]) node.child[idx] = new TrieNode();
            node = node.child[idx]!;
        }
        node.isEnd = true;
    }

    function dfs(word: string, i: number, node: TrieNode | null, cnt: number): boolean {
        if (cnt > 2 || !node) return false;
        if (i === word.length) return node.isEnd;

        const idx = word.charCodeAt(i) - 97;

        // 不修改
        if (node.child[idx] && dfs(word, i + 1, node.child[idx], cnt))
            return true;

        // 修改
        if (cnt < 2) {
            for (let c = 0; c < 26; c++) {
                if (c === idx) continue;
                if (node.child[c] && dfs(word, i + 1, node.child[c], cnt + 1))
                    return true;
            }
        }

        return false;
    }

    for (const w of dictionary) insert(w);

    const res: string[] = [];
    for (const q of queries) {
        if (dfs(q, 0, root, 0)) res.push(q);
    }

    return res;
}

###Rust

struct TrieNode {
    child: Vec<Option<Box<TrieNode>>>,
    is_end: bool,
}

impl TrieNode {
    fn new() -> Self {
        let mut child = Vec::with_capacity(26);
        for _ in 0..26 {
            child.push(None);
        }

        Self {
            child,
            is_end: false,
        }
    }
}

impl Solution {
    fn insert(root: &mut TrieNode, word: &str) {
        let mut node = root;
        for c in word.chars() {
            let idx = (c as u8 - b'a') as usize;
            if node.child[idx].is_none() {
                node.child[idx] = Some(Box::new(TrieNode::new()));
            }
            node = node.child[idx].as_mut().unwrap();
        }
        node.is_end = true;
    }

    fn dfs(word: &[u8], i: usize, node: &TrieNode, cnt: i32) -> bool {
        if cnt > 2 {
            return false;
        }
        if i == word.len() {
            return node.is_end;
        }

        let idx = (word[i] - b'a') as usize;

        // 不修改
        if let Some(ref next) = node.child[idx] {
            if Self::dfs(word, i + 1, next, cnt) {
                return true;
            }
        }

        // 修改
        if cnt < 2 {
            for c in 0..26 {
                if c == idx {
                    continue;
                }
                if let Some(ref next) = node.child[c] {
                    if Self::dfs(word, i + 1, next, cnt + 1) {
                        return true;
                    }
                }
            }
        }

        false
    }

    pub fn two_edit_words(queries: Vec<String>, dictionary: Vec<String>) -> Vec<String> {
        let mut root = TrieNode::new();

        for w in &dictionary {
            Self::insert(&mut root, w);
        }

        let mut res = vec![];
        for q in &queries {
            if Self::dfs(q.as_bytes(), 0, &root, 0) {
                res.push(q.clone());
            }
        }

        res
    }
}

复杂度分析

  • 时间复杂度:$O(k \cdot n + q \cdot n^2 \cdot 25^2)$,其中 $q$ 为 $\textit{queries}$ 的长度,$k$ 为 $\textit{dictionary}$ 的长度,$n$ 为 $\textit{queries}[i]$ 的长度。建字典树需要 $O(kn)$,查询时对于每一个字母都有修改和不修改两种选择,选择修改位置有 $C_n^2=n^2$ 种选择,其中不修改有 $1$ 条分支,修改有 $25$ 条分支,最多修改两次,因此有 $25^2$ 种选择。
  • 空间复杂度:$O(kn)$。即为字典树所占用的空间。

每日一题-距离字典两次编辑以内的单词🟡

2026年4月22日 00:00

给你两个字符串数组 queries 和 dictionary 。数组中所有单词都只包含小写英文字母,且长度都相同。

一次 编辑 中,你可以从 queries 中选择一个单词,将任意一个字母修改成任何其他字母。从 queries 中找到所有满足以下条件的字符串:不超过 两次编辑内,字符串与 dictionary 中某个字符串相同。

请你返回 queries 中的单词列表,这些单词距离 dictionary 中的单词 编辑次数 不超过 两次 。单词返回的顺序需要与 queries 中原本顺序相同。

 

示例 1:

输入:queries = ["word","note","ants","wood"], dictionary = ["wood","joke","moat"]
输出:["word","note","wood"]
解释:
- 将 "word" 中的 'r' 换成 'o' ,得到 dictionary 中的单词 "wood" 。
- 将 "note" 中的 'n' 换成 'j' 且将 't' 换成 'k' ,得到 "joke" 。
- "ants" 需要超过 2 次编辑才能得到 dictionary 中的单词。
- "wood" 不需要修改(0 次编辑),就得到 dictionary 中相同的单词。
所以我们返回 ["word","note","wood"] 。

示例 2:

输入:queries = ["yes"], dictionary = ["not"]
输出:[]
解释:
"yes" 需要超过 2 次编辑才能得到 "not" 。
所以我们返回空数组。

 

提示:

  • 1 <= queries.length, dictionary.length <= 100
  • n == queries[i].length == dictionary[j].length
  • 1 <= n <= 100
  • 所有 queries[i] 和 dictionary[j] 都只包含小写英文字母。

暴力(Python/Java/C++/C/Go/JS/Rust)

作者 endlesscheng
2022年10月30日 07:58

对于每个 $q = \textit{queries}[i]$,遍历 $\textit{dictionary}$ 中的字符串 $s$,判断 $q$ 和 $s$ 是否至多有两个位置上的字母不同。

class Solution:
    def twoEditWords(self, queries: List[str], dictionary: List[str]) -> List[str]:
        ans = []
        for q in queries:
            for s in dictionary:
                if sum(x != y for x, y in zip(q, s)) <= 2:
                    ans.append(q)
                    break
        return ans
class Solution {
    public List<String> twoEditWords(String[] queries, String[] dictionary) {
        List<String> ans = new ArrayList<>();
        for (String q : queries) {
            for (String s : dictionary) {
                int cnt = 0;
                for (int i = 0; i < s.length() && cnt <= 2; i++) {
                    if (q.charAt(i) != s.charAt(i)) {
                        cnt++;
                    }
                }
                if (cnt <= 2) {
                    ans.add(q);
                    break;
                }
            }
        }
        return ans;
    }
}
class Solution {
public:
    vector<string> twoEditWords(vector<string>& queries, vector<string>& dictionary) {
        vector<string> ans;
        for (auto& q : queries) {
            for (auto& s : dictionary) {
                int cnt = 0;
                for (int i = 0; i < s.size() && cnt <= 2; i++) {
                    if (q[i] != s[i]) {
                        cnt++;
                    }
                }
                if (cnt <= 2) {
                    ans.push_back(q);
                    break;
                }
            }
        }
        return ans;
    }
};
char** twoEditWords(char** queries, int queriesSize, char** dictionary, int dictionarySize, int* returnSize) {
    char** ans = malloc(queriesSize * sizeof(char*));
    *returnSize = 0;

    for (int i = 0; i < queriesSize; i++) {
        char* q = queries[i];
        for (int j = 0; j < dictionarySize; j++) {
            char* s = dictionary[j];
            int cnt = 0;
            for (int k = 0; s[k] && cnt <= 2; k++) {
                if (q[k] != s[k]) {
                    cnt++;
                }
            }
            if (cnt <= 2) {
                ans[(*returnSize)++] = q;
                break;
            }
        }
    }

    return ans;
}
func twoEditWords(queries, dictionary []string) (ans []string) {
for _, q := range queries {
next:
for _, s := range dictionary {
cnt := 0
for i := range s {
if q[i] != s[i] {
cnt++
if cnt > 2 {
continue next
}
}
}
ans = append(ans, q)
break
}
}
return
}
var twoEditWords = function(queries, dictionary) {
    const ans = [];
    for (const q of queries) {
        for (const s of dictionary) {
            let cnt = 0;
            for (let i = 0; i < s.length && cnt <= 2; i++) {
                if (q[i] !== s[i]) {
                    cnt++;
                }
            }
            if (cnt <= 2) {
                ans.push(q);
                break;
            }
        }
    }
    return ans;
};
impl Solution {
    pub fn two_edit_words(queries: Vec<String>, dictionary: Vec<String>) -> Vec<String> {
        let mut ans = vec![];
        for q in queries {
            for s in &dictionary {
                let mut cnt = 0;
                for (a, b) in q.bytes().zip(s.bytes()) {
                    if a != b {
                        cnt += 1;
                        if cnt > 2 {
                            break;
                        }
                    }
                }
                if cnt <= 2 {
                    ans.push(q);
                    break;
                }
            }
        }
        ans
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(qdn)$,其中 $q$ 是 $\textit{queries}$ 的长度,$d$ 是 $\textit{dictionary}$ 的长度,$n$ 是 $\textit{queries}[i]$ 的长度。题目保证所有字符串长度相等。
  • 空间复杂度:$\mathcal{O}(1)$。返回值不计入。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

随便做

作者 qian-li-ma-8
2022年10月30日 00:08

解题思路

代码

###python3

class Solution:
    def twoEditWords(self, queries: List[str], dictionary: List[str]) -> List[str]:
        def check(x,y):
            t=0
            for i in range(len(x)):
                if x[i]!=y[i]:
                    t+=1
            return t<=2
        covered=set()
        lst=[]
        for i in dictionary:
            for j in range(len(queries)):
                t=queries[j]
                if j not in covered and check(t,i):
                    covered.add(j)
                    lst.append(j)
        lst.sort()
        return [queries[i] for i in lst]
昨天以前LeetCode 每日一题题解

每日一题-执行交换操作后的最小汉明距离🟡

2026年4月21日 00:00

给你两个整数数组 sourcetarget ,长度都是 n 。还有一个数组 allowedSwaps ,其中每个 allowedSwaps[i] = [ai, bi] 表示你可以交换数组 source 中下标为 aibi下标从 0 开始)的两个元素。注意,你可以按 任意 顺序 多次 交换一对特定下标指向的元素。

相同长度的两个数组 sourcetarget 间的 汉明距离 是元素不同的下标数量。形式上,其值等于满足 source[i] != target[i]下标从 0 开始)的下标 i0 <= i <= n-1)的数量。

在对数组 source 执行 任意 数量的交换操作后,返回 sourcetarget 间的 最小汉明距离

 

示例 1:

输入:source = [1,2,3,4], target = [2,1,4,5], allowedSwaps = [[0,1],[2,3]]
输出:1
解释:source 可以按下述方式转换:
- 交换下标 0 和 1 指向的元素:source = [2,1,3,4]
- 交换下标 2 和 3 指向的元素:source = [2,1,4,3]
source 和 target 间的汉明距离是 1 ,二者有 1 处元素不同,在下标 3 。

示例 2:

输入:source = [1,2,3,4], target = [1,3,2,4], allowedSwaps = []
输出:2
解释:不能对 source 执行交换操作。
source 和 target 间的汉明距离是 2 ,二者有 2 处元素不同,在下标 1 和下标 2 。

示例 3:

输入:source = [5,1,2,4,3], target = [1,5,4,2,3], allowedSwaps = [[0,4],[4,2],[1,3],[1,4]]
输出:0

 

提示:

  • n == source.length == target.length
  • 1 <= n <= 105
  • 1 <= source[i], target[i] <= 105
  • 0 <= allowedSwaps.length <= 105
  • allowedSwaps[i].length == 2
  • 0 <= ai, bi <= n - 1
  • ai != bi

建图 + DFS(Python/Java/C++/Go)

作者 endlesscheng
2026年4月14日 08:20

在 $a_i$ 和 $b_i$ 之间连边,得到一个无向图 $G$。

$G$ 的同一个连通块中的节点(下标)可以随意交换。

证明(具体交换方案)

例如 $\textit{source}$ 在某个连通块中的元素为 $1,1,2,3$,$\textit{target}$ 在这个连通块中的元素为 $2,2,4,1$,二者有一个共同的 $1$ 和一个共同的 $2$。但 $\textit{source}$ 多了一个 $1$ 和一个 $3$,$\textit{target}$ 多了一个 $2$ 和一个 $4$,每对多出来的元素,无法匹配,会贡献 $1$ 个汉明距离。所以这个连通块贡献了 $2$ 个汉明距离。

用哈希表(或者数组)统计 $\textit{source}$ 和 $\textit{target}$ 各自多出来的元素个数。总个数除以 $2$,即为无法配对的元素对的个数,即汉明距离。

###py

class Solution:
    def minimumHammingDistance(self, source: List[int], target: List[int], allowedSwaps: List[List[int]]) -> int:
        n = len(source)
        g = [[] for _ in range(n)]
        for i, j in allowedSwaps:
            g[i].append(j)  # 建图
            g[j].append(i)

        def dfs(x: int) -> None:
            vis[x] = True  # 避免重复访问
            # 抵消相同的元素,最终剩下 source 和 target 各自多出来的元素(对称差)
            diff[source[x]] += 1
            diff[target[x]] -= 1
            for y in g[x]:
                if not vis[y]:
                    dfs(y)

        vis = [False] * n
        ans = 0
        for x in range(n):
            if not vis[x]:
                diff = defaultdict(int)
                dfs(x)
                ans += sum(map(abs, diff.values()))
        return ans // 2  # 有 ans // 2 对多出来的元素

###java

class Solution {
    public int minimumHammingDistance(int[] source, int[] target, int[][] allowedSwaps) {
        int n = source.length;
        List<Integer>[] g = new ArrayList[n];
        Arrays.setAll(g, _ -> new ArrayList<>());
        for (int[] e : allowedSwaps) {
            int i = e[0];
            int j = e[1];
            g[i].add(j); // 建图
            g[j].add(i);
        }

        boolean[] vis = new boolean[n];
        int ans = 0;
        for (int x = 0; x < n; x++) {
            if (!vis[x]) {
                Map<Integer, Integer> diff = new HashMap<>();
                dfs(x, source, target, g, vis, diff);
                for (int c : diff.values()) {
                    ans += Math.abs(c);
                }
            }
        }
        return ans / 2; // 有 ans / 2 对多出来的元素
    }

    private void dfs(int x, int[] source, int[] target, List<Integer>[] g, boolean[] vis, Map<Integer, Integer> diff) {
        vis[x] = true; // 避免重复访问
        // 抵消相同的元素,最终剩下 source 和 target 各自多出来的元素(对称差)
        diff.merge(source[x], 1, Integer::sum);  // diff[source[x]]++;
        diff.merge(target[x], -1, Integer::sum); // diff[target[x]]--;
        for (int y : g[x]) {
            if (!vis[y]) {
                dfs(y, source, target, g, vis, diff);
            }
        }
    }
}

###cpp

class Solution {
public:
    int minimumHammingDistance(vector<int>& source, vector<int>& target, vector<vector<int>>& allowedSwaps) {
        int n = source.size();
        vector<vector<int>> g(n);
        for (auto& e : allowedSwaps) {
            int i = e[0], j = e[1];
            g[i].push_back(j); // 建图
            g[j].push_back(i);
        }

        vector<int8_t> vis(n);
        unordered_map<int, int> diff;

        auto dfs = [&](this auto&& dfs, int x) -> void {
            vis[x] = true; // 避免重复访问
            // 抵消相同的元素,最终剩下 source 和 target 各自多出来的元素(对称差)
            diff[source[x]]++;
            diff[target[x]]--;
            for (int y : g[x]) {
                if (!vis[y]) {
                    dfs(y);
                }
            }
        };

        int ans = 0;
        for (int x = 0; x < n; x++) {
            if (!vis[x]) {
                diff.clear();
                dfs(x);
                for (auto& [_, c] : diff) {
                    ans += abs(c);
                }
            }
        }
        return ans / 2; // 有 ans / 2 对多出来的元素
    }
};

###go

func minimumHammingDistance(source []int, target []int, allowedSwaps [][]int) (ans int) {
n := len(source)
g := make([][]int, n)
for _, e := range allowedSwaps {
i, j := e[0], e[1]
g[i] = append(g[i], j) // 建图
g[j] = append(g[j], i)
}

vis := make([]bool, n)
diff := map[int]int{}

var dfs func(int)
dfs = func(x int) {
vis[x] = true // 避免重复访问
// 抵消相同的元素,最终剩下 source 和 target 各自多出来的元素(对称差)
diff[source[x]]++
diff[target[x]]--
for _, y := range g[x] {
if !vis[y] {
dfs(y)
}
}
}

for x, b := range vis {
if !b {
clear(diff)
dfs(x)
for _, c := range diff {
ans += abs(c)
}
}
}
return ans / 2 // 有 ans / 2 对多出来的元素
}

func abs(x int) int {
if x < 0 {
return -x
}
return x
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(n + m)$,其中 $n$ 是 $\textit{source}$ 的长度,$m$ 是 $\textit{allowedSwaps}$ 的长度。
  • 空间复杂度:$\mathcal{O}(n + m)$。

相似题目

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

Python3 并查集+哈希表

作者 yim-6
2021年1月10日 16:38

解题思路

  1. 参数定义
    • parent:并查集数组,每个节点指向根节点
    • dic:哈希表,k,v分别为并查集中每个簇的根节点和簇中所有元素的索引
    • res:返回值
  2. 思路
    • 示例:如果allowedSwaps[[0,1],[1,2]],说明索引0/11/2处的元素可以交换,根据连通性,0/2处的元素同样可以交换,此时0/1/2构成的连通块之间相互可以交换,所以可以利用并查集计算出每一个连通块。
    • 首先确定每个元素对应的根节点,利用哈希表dic存储根节点对应的所有元素的索引。
    • 遍历dick,v分别为根节点和簇元素索引集合,找到索引集合vsourcetarget中对应的元素a,b,利用ab中查找差集,所以b可以为哈希表,记录了每个元素的个数,便于O(1)时间复杂度查找。如果在b中没有找到a中的元素,res+=1。求两个数组的交集也可以用哈希表的&运算,见代码二
  3. 复杂度分析
    • 时间复杂度:O(N+M)Nsourcetarget的长度,MallowedSwaps长度,路径压缩时间α(N)很小可忽略
    • 空间复杂度:O(N)

代码

###python3

class Solution:
    def minimumHammingDistance(self, source: List[int], target: List[int], allowedSwaps: List[List[int]]) -> int:
        n=len(source)
        parent={i:i for i in range(n)}
        # 并查集
        def find(x):
            if x!=parent[x]:
                parent[x]=find(parent[x])
            return parent[x]
        # 搜索根节点
        for l,r in allowedSwaps:
            a,b=find(l),find(r)
            if a!=b:
                parent[b]=a
        # 获取根节点对应的连通块
        dic=collections.defaultdict(list)
        for i in range(n):
            a=find(i)
            dic[a].append(i)
        res=0
        # 计算每个连通块对应的source元素与target的差集
        for k,v in dic.items():
            a=[source[i] for i in v]
            b=Counter([target[i] for i in v])
            for c in a:
                if b[c]>0:
                    b[c]-=1
                else:
                    res+=1
        return res

###python3

class Solution:
    def minimumHammingDistance(self, source: List[int], target: List[int], allowedSwaps: List[List[int]]) -> int:
        n=len(source)
        parent={i:i for i in range(n)}
        # 并查集
        def find(x):
            if x!=parent[x]:
                parent[x]=find(parent[x])
            return parent[x]
        # 搜索根节点
        for l,r in allowedSwaps:
            a,b=find(l),find(r)
            if a!=b:
                parent[b]=a
        # 获取根节点对应的连通块
        dic=collections.defaultdict(list)
        for i in range(n):
            a=find(i)
            dic[a].append(i)
        # 计算每个连通块对应的source元素与target的差集
        count=0
        for k,v in dic.items():
            a=Counter([source[i] for i in v])
            b=Counter([target[i] for i in v])
            count+=len(list((a&b).elements()))
        return n-count

【并查集】要稍微动点脑筋

作者 Arsenal-591
2021年1月10日 12:09

根据题意不难注意到,如果 $1,2$ 之间可以交换,$2,3$ 之间可以交换,那么即使 $[1,3]$ 未出现在 $\textit{allowedSwaps}$ 数组中,$1,3$ 之间也是可以交换的。

因此,我们使用并查集来维护这一关系:对于 $\textit{source}$ 数组中两个任意的位置 $i,j$,如果 $i,j$ 在同一个联通分支里,那么 $i,j$ 之间就是可以交换的。于是,需要首先遍历 $\textit{allowedSwaps}$ 数组中所有元素,从而构建 $\textit{source}$ 数组中位置之间的联通关系。

对于任意的联通分支 $k$,由于它们内部的位置之间可以任意交换,因此它们初始出现在 $\textit{source}$ 数组中的顺序并不重要。故我们为每个联通分支 $k$ 维护 $\textit{source}$ 中对应位置元素的集合,以及 $\textit{target}$ 中对应位置元素的集合。随后,汉明距离的最小值,就是这两个集合之间不同的元素的数量。

那么两个集合之间不同元素的数量呢?我们遍历第一个集合中的每个元素:

  • 如果该元素出现在第二个集合中,就将该元素从第二个集合中删除
  • 否则,如果没有出现,则将计数器加 $1$

值得注意的是,在本题中,我们需要允许集合中的元素出现重复。在下面的代码中,我们使用 C++ 中的 unordered_multiset 数据结构。

class Solution {
public:
    int getf(vector<int>& f, int x) {
        if (f[x] == x) return x;
        int nf = getf(f, f[x]);
        f[x] = nf;
        return nf;
    }
    
    void add(vector<int>& f, int x, int y) {
        int fx = getf(f, x);
        int fy = getf(f, y);
        f[fx] = fy;
    }
    
    int minimumHammingDistance(vector<int>& source, vector<int>& target, vector<vector<int>>& allowedSwaps) {
        int n = source.size();
        vector<int> f(n);
        for (int i = 0; i < n; i++) {
            f[i] = i;
        }
        for (const auto& e: allowedSwaps) {
            add(f, e[0], e[1]);
        }
        
        unordered_map<int, unordered_multiset<int>> s, t; // 为每个联通分支维护位置的集合
        for (int i = 0; i < n; i++) {
            int fa = getf(f, i);
            s[fa].insert(source[i]);
            t[fa].insert(target[i]);
        }
        
        int ret = 0;
        for (int i = 0; i < n; i++) {
            if (s.find(i) == s.end()) continue;
            for (int x: s[i]) {
                if (t[i].find(x) == t[i].end()) {
                    ret++;
                } else {
                    // 不能使用 t[i].erase(x),不然会删掉所有的 x
                    t[i].erase(t[i].find(x));
                }
            }
        }
        return ret;
    }
};

每日一题-两栋颜色不同且距离最远的房子🟢

2026年4月20日 00:00

街上有 n 栋房子整齐地排成一列,每栋房子都粉刷上了漂亮的颜色。给你一个下标从 0 开始且长度为 n 的整数数组 colors ,其中 colors[i] 表示第  i 栋房子的颜色。

返回 两栋 颜色 不同 房子之间的 最大 距离。

i 栋房子和第 j 栋房子之间的距离是 abs(i - j) ,其中 abs(x)x 的绝对值。

 

示例 1:

输入:colors = [1,1,1,6,1,1,1]
输出:3
解释:上图中,颜色 1 标识成蓝色,颜色 6 标识成红色。
两栋颜色不同且距离最远的房子是房子 0 和房子 3 。
房子 0 的颜色是颜色 1 ,房子 3 的颜色是颜色 6 。两栋房子之间的距离是 abs(0 - 3) = 3 。
注意,房子 3 和房子 6 也可以产生最佳答案。

示例 2:

输入:colors = [1,8,3,8,3]
输出:4
解释:上图中,颜色 1 标识成蓝色,颜色 8 标识成黄色,颜色 3 标识成绿色。
两栋颜色不同且距离最远的房子是房子 0 和房子 4 。
房子 0 的颜色是颜色 1 ,房子 4 的颜色是颜色 3 。两栋房子之间的距离是 abs(0 - 4) = 4 。

示例 3:

输入:colors = [0,1]
输出:1
解释:两栋颜色不同且距离最远的房子是房子 0 和房子 1 。
房子 0 的颜色是颜色 0 ,房子 1 的颜色是颜色 1 。两栋房子之间的距离是 abs(0 - 1) = 1 。

 

提示:

  • n == colors.length
  • 2 <= n <= 100
  • 0 <= colors[i] <= 100
  • 生成的测试数据满足 至少 存在 2 栋颜色不同的房子

[知心猛男] 详解一下贪心

作者 mochi-ds
2021年11月21日 19:18

题意就是找数列中距离最远的两个不同的数字, 显然我们有时间复杂度O(N)的解法:

考虑最终答案的长度:
显然答案最大可能长度是n-1,即:首尾不同
如果不是n-1,那就是首尾相同,则最大可能长度就是n-2,即:尾-1和首不同 或者 首+1和尾不同
如果也不是n-2,那就是首尾和首+1和尾-1位置的数都相同,则最大可能长度就是n-3, 即首+2和尾不同 或者 尾-2和首不同
如果也不是n-3,那就是……
……
依次类推,我们发现从最终可能答案考虑,用两个指针 i 和 j 同时从两端向中间移动,每次各移动一步,当出现和首或者尾不同的数字的时候,最大可能长度就出现了:n-1-i 或者 j,因为他们是相等的(i+j==n-1)。所以直接break输出即可。

###cpp

class Solution {
public:
    int maxDistance(vector<int>& colors) {
        int n = colors.size();        
        int i, j;
        for(i = 0, j = n-1; i < j; ++i, --j) //由于一定存在不同的颜色,所以当i==j时候一定就是不同的颜色
            if(colors[i] != colors[n-1] || colors[j] != colors[0]) break;
        return j;
    }
};

两种方法(暴力 / 贪心)【Java】

2021年11月21日 12:25

方法1

  • 暴力
  • 使用两个for,比较判断0~length-1、1~length-1、2~length-1...找到最大的值

代码

###java

class Solution {
    public int maxDistance(int[] colors) {
        int length = colors.length;
        int max = -1;
        
        for (int i = 0; i < length; i++) {
            for (int j = length-1; j > 0; j--) {
                if (colors[i] != colors[j] && j-i > max) {
                    max = j-i;
                }
            }
        }
        
        return max;
    }
}

方法2

  • 贪心
  • 因为要最大,所以有三种情况:
    1. 首尾不相同直接返回,否则说明首尾是相同的颜色
    2. “0~右往左第一个不相同”这一段
    3. “左往右第一个不相同~length-1”这一段
    4. 然后比较这两个谁大就行
      image.png

代码

###java

class Solution {
    public int maxDistance(int[] colors) {
        int length = colors.length;

        // 如果首位颜色不同直接返回
        if (colors[0] != colors[length - 1]) {
            return length - 1;
        }
        
        // 获取左边第一个不相同的位置
        int left = 1;
        while (colors[left] == colors[0]) {
            left += 1;
        }
        // 获取右边第一个不相同的位置
        int right = length - 2;
        while (colors[right] == colors[length - 1]) {
            right -= 1;
        }

        // 0~right 的长度 和 left~length-1 的长度取最大值
        // 因为要最大,所以不可能在中间,要么就是左边,要么就是右边
        return Math.max(right, length - 1 - left);
    }
}

O(n) 做法,脑筋急转弯(Python/Java/C++/C/Go/JS/Rust)

作者 endlesscheng
2021年11月21日 12:18

如果 $\textit{colors}[0] \ne \textit{colors}[n-1]$,那么答案是 $n-1$。

否则设 $c = \textit{colors}[0] = \textit{colors}[n-1]$。

设最大距离来自房子 $i$ 和房子 $j$。由于题目要求 $\textit{colors}[i]\ne \textit{colors}[j]$,所以这两个颜色不可能都等于 $c$。如果 $\textit{colors}[i]\ne c$,那么 $j$ 必然是离 $i$ 最远的 $0$ 或者 $n-1$(这两栋房子的颜色与房子 $i$ 不同)。这意味着,$0$ 或者 $n-1$ 必然参与最大距离的计算

  • 对于房子 $0$ 来说,另一栋房子越远(越靠右)越好。从右往左找到颜色不等于 $c$ 的房子 $\textit{colors}[r]$,距离为 $r-0 = r$。
  • 对于房子 $n-1$ 来说,另一栋房子越远(越靠左)越好。从左往右找到颜色不等于 $c$ 的房子 $\textit{colors}[\ell]$,距离为 $n-1-\ell$。

答案为二者的最大值

$$
\max(r, n-1-\ell)
$$

注意题目保证至少有两栋颜色不同的房子。

class Solution:
    def maxDistance(self, colors: List[int]) -> int:
        n = len(colors)
        c = colors[0]
        if c != colors[-1]:
            return n - 1

        # 找最右边的颜色不等于 c 的房子
        # 题目保证至少有两栋颜色不同的房子
        r = n - 2
        while colors[r] == c:
            r -= 1

        # 找最左边的颜色不等于 c 的房子
        l = 1
        while colors[l] == c:
            l += 1

        return max(r, n - 1 - l)
class Solution {
    public int maxDistance(int[] colors) {
        int n = colors.length;
        int c = colors[0];
        if (c != colors[n - 1]) {
            return n - 1;
        }

        // 找最右边的颜色不等于 c 的房子
        // 题目保证至少有两栋颜色不同的房子
        int r = n - 2;
        while (colors[r] == c) {
            r--;
        }

        // 找最左边的颜色不等于 c 的房子
        int l = 1;
        while (colors[l] == c) {
            l++;
        }

        return Math.max(r, n - 1 - l);
    }
}
class Solution {
public:
    int maxDistance(vector<int>& colors) {
        int n = colors.size();
        int c = colors[0];
        if (c != colors[n - 1]) {
            return n - 1;
        }

        // 找最右边的颜色不等于 c 的房子
        // 题目保证至少有两栋颜色不同的房子
        int r = n - 2;
        while (colors[r] == c) {
            r--;
        }

        // 找最左边的颜色不等于 c 的房子
        int l = 1;
        while (colors[l] == c) {
            l++;
        }

        return max(r, n - 1 - l);
    }
};
int maxDistance(int* colors, int colorsSize) {
    int n = colorsSize;
    int c = colors[0];
    if (c != colors[n - 1]) {
        return n - 1;
    }

    // 找最右边的颜色不等于 c 的房子
    // 题目保证至少有两栋颜色不同的房子
    int r = n - 2;
    while (colors[r] == c) {
        r--;
    }

    // 找最左边的颜色不等于 c 的房子
    int l = 1;
    while (colors[l] == c) {
        l++;
    }

    return MAX(r, n - 1 - l);
}
func maxDistance(colors []int) int {
n := len(colors)
c := colors[0]
if c != colors[n-1] {
return n - 1
}

// 找最右边的颜色不等于 c 的房子
// 题目保证至少有两栋颜色不同的房子
r := n - 2
for colors[r] == c {
r--
}

// 找最左边的颜色不等于 c 的房子
l := 1
for colors[l] == c {
l++
}

return max(r, n-1-l)
}
var maxDistance = function(colors) {
    const n = colors.length;
    const c = colors[0];
    if (c !== colors[n - 1]) {
        return n - 1;
    }

    // 找最右边的颜色不等于 c 的房子
    // 题目保证至少有两栋颜色不同的房子
    let r = n - 2;
    while (colors[r] === c) {
        r--;
    }

    // 找最左边的颜色不等于 c 的房子
    let l = 1;
    while (colors[l] === c) {
        l++;
    }

    return Math.max(r, n - 1 - l);
};
impl Solution {
    pub fn max_distance(colors: Vec<i32>) -> i32 {
        let n = colors.len();
        let c = colors[0];
        if c != colors[n - 1] {
            return (n - 1) as _;
        }

        // 找最右边的颜色不等于 c 的房子
        // 题目保证至少有两栋颜色不同的房子
        let mut r = n - 2;
        while colors[r] == c {
            r -= 1;
        }

        // 找最左边的颜色不等于 c 的房子
        let mut l = 1;
        while colors[l] == c {
            l += 1;
        }

        r.max(n - 1 - l) as _
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(n)$,其中 $n$ 是 $\textit{colors}$ 的长度。
  • 空间复杂度:$\mathcal{O}(1)$。

专题训练

见下面贪心与思维题单的「§5.2 脑筋急转弯」。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

每日一题-下标对中的最大距离🟡

2026年4月19日 00:00

给你两个 非递增 的整数数组 nums1nums2 ,数组下标均 从 0 开始 计数。

下标对 (i, j)0 <= i < nums1.length0 <= j < nums2.length 。如果该下标对同时满足 i <= jnums1[i] <= nums2[j] ,则称之为 有效 下标对,该下标对的 距离j - i

返回所有 有效 下标对 (i, j) 中的 最大距离 。如果不存在有效下标对,返回 0

一个数组 arr ,如果每个 1 <= i < arr.length 均有 arr[i-1] >= arr[i] 成立,那么该数组是一个 非递增 数组。

 

示例 1:

输入:nums1 = [55,30,5,4,2], nums2 = [100,20,10,10,5]
输出:2
解释:有效下标对是 (0,0), (2,2), (2,3), (2,4), (3,3), (3,4) 和 (4,4) 。
最大距离是 2 ,对应下标对 (2,4) 。

示例 2:

输入:nums1 = [2,2,2], nums2 = [10,10,1]
输出:1
解释:有效下标对是 (0,0), (0,1) 和 (1,1) 。
最大距离是 1 ,对应下标对 (0,1) 。

示例 3:

输入:nums1 = [30,29,19,5], nums2 = [25,25,25,25,25]
输出:2
解释:有效下标对是 (2,2), (2,3), (2,4), (3,3) 和 (3,4) 。
最大距离是 2 ,对应下标对 (2,4) 。

 

提示:

  • 1 <= nums1.length <= 105
  • 1 <= nums2.length <= 105
  • 1 <= nums1[i], nums2[j] <= 105
  • nums1nums2 都是 非递增 数组

下标对中的最大距离

2021年5月9日 13:39

方法一:双指针

提示 $1$

考虑遍历下标对中的某一个下标,并寻找此时所有有效坐标对中距离最大的另一个下标。暴力遍历另一个下标显然不满足时间复杂度要求,此时是否存在一些可以优化寻找过程的性质?

思路与算法

不失一般性,我们遍历 $\textit{nums}_2$ 中的下标 $j$,同时寻找此时符合要求的 $\textit{nums}_1$ 中最小的下标 $i$。

假设下标 $j$ 对应的最小下标为 $i$,当 $j$ 变为 $j + 1$ 时,由于 $\textit{nums}_2$ 非递增,即 $\textit{nums}_2[j] \ge \textit{nums}_2[j+1]$,那么 $\textit{nums}_1$ 中可取元素的上界不会增加。同时由于 $\textit{nums}_1$ 也非递增,因此 $j + 1$ 对应的最小下标 $i'$ 一定满足 $i' \ge i$。

那么我们就可以在遍历 $j$ 的同时维护对应的 $i$,并用 $\textit{res}$ 来维护下标对 $(i, j)$ 的最大距离。我们将 $\textit{res}$ 初值置为 $0$,这样即使存在 $\textit{nums}_1[i] \le \textit{nums}_2[j]$ 但 $i > j$ 这种不符合要求的情况,由于此时距离为负因而不会对结果产生影响(不存在时也返回 $0$)。

另外,在维护最大距离的时候要注意下标 $i$ 的合法性,即 $i < n_1$,其中 $n_1$ 为 $\textit{nums}_1$ 的长度。

代码

###C++

class Solution {
public:
    int maxDistance(vector<int>& nums1, vector<int>& nums2) {
        int n1 = nums1.size();
        int n2 = nums2.size();
        int i = 0;
        int res = 0;
        for (int j = 0; j < n2; ++j){
            while (i < n1 && nums1[i] > nums2[j]){
                ++i;
            }
            if (i < n1){
                res = max(res, j - i);
            }
        }
        return res;
    }
};

###Python

class Solution:
    def maxDistance(self, nums1: List[int], nums2: List[int]) -> int:
        n1, n2 = len(nums1), len(nums2)
        i = 0
        res = 0
        for j in range(n2):
            while i < n1 and nums1[i] > nums2[j]:
                i += 1
            if i < n1:
                res = max(res, j - i)
        return res

###Java

class Solution {
    public int maxDistance(int[] nums1, int[] nums2) {
        int n1 = nums1.length;
        int n2 = nums2.length;
        int i = 0;
        int res = 0;
        
        for (int j = 0; j < n2; j++) {
            while (i < n1 && nums1[i] > nums2[j]) {
                i++;
            }
            if (i < n1) {
                res = Math.max(res, j - i);
            }
        }
        
        return res;
    }
}

###C#

public class Solution {
    public int MaxDistance(int[] nums1, int[] nums2) {
        int n1 = nums1.Length;
        int n2 = nums2.Length;
        int i = 0;
        int res = 0;
        
        for (int j = 0; j < n2; j++) {
            while (i < n1 && nums1[i] > nums2[j]) {
                i++;
            }
            if (i < n1) {
                res = Math.Max(res, j - i);
            }
        }
        
        return res;
    }
}

###Go

func maxDistance(nums1 []int, nums2 []int) int {
    n1 := len(nums1)
    n2 := len(nums2)
    i := 0
    res := 0
    
    for j := 0; j < n2; j++ {
        for i < n1 && nums1[i] > nums2[j] {
            i++
        }
        if i < n1 {
            if j-i > res {
                res = j - i
            }
        }
    }
    
    return res
}

###C

int maxDistance(int* nums1, int nums1Size, int* nums2, int nums2Size) {
    int i = 0;
    int res = 0;
    
    for (int j = 0; j < nums2Size; j++) {
        while (i < nums1Size && nums1[i] > nums2[j]) {
            i++;
        }
        if (i < nums1Size) {
            if (j - i > res) {
                res = j - i;
            }
        }
    }
    
    return res;
}

###JavaScript

var maxDistance = function(nums1, nums2) {
    const n1 = nums1.length;
    const n2 = nums2.length;
    let i = 0;
    let res = 0;
    
    for (let j = 0; j < n2; j++) {
        while (i < n1 && nums1[i] > nums2[j]) {
            i++;
        }
        if (i < n1) {
            res = Math.max(res, j - i);
        }
    }
    
    return res;
};

###TypeScript

function maxDistance(nums1: number[], nums2: number[]): number {
    const n1 = nums1.length;
    const n2 = nums2.length;
    let i = 0;
    let res = 0;
    
    for (let j = 0; j < n2; j++) {
        while (i < n1 && nums1[i] > nums2[j]) {
            i++;
        }
        if (i < n1) {
            res = Math.max(res, j - i);
        }
    }
    
    return res;
};

###Rust

impl Solution {
    pub fn max_distance(nums1: Vec<i32>, nums2: Vec<i32>) -> i32 {
        let n1 = nums1.len();
        let n2 = nums2.len();
        let mut i = 0;
        let mut res = 0;
        
        for j in 0..n2 {
            while i < n1 && nums1[i] > nums2[j] {
                i += 1;
            }
            if i < n1 {
                res = res.max((j as i32) - (i as i32));
            }
        }
        
        res
    }
}

复杂度分析

  • 时间复杂度:$O(n_1 + n_2)$,其中 $n_1, n_2$ 分别为 $\textit{nums}_1$ 与 $\textit{nums}_2$ 的长度。在双指针寻找最大值的过程中,我们最多会遍历两个数组各一次。

  • 空间复杂度:$O(1)$,我们使用了常数个变量进行遍历。

❌
❌