视频拼接 图解题解
这道题到底在问什么
- 输入
- clips=[[0,2],[4,6],[8,10],[1,9],[1,5],[5,9]], time=10
- 输出
- 3
最优解:一步一步想明白
- 3记住这句:last 记每个起点的最远延伸,贪心扫描时走到当前段右端就接一段、跳到段内最远 mx。后面每帧都在套它。
- 4先开一个长度 10 的数组 last,last[i] 记「从位置 i 起跳一步最远能到哪」,一开始都填 0。
- 5看片段 [0,2]:从位置 0 起跳能延伸到 2,比原来的 0 远,last[0] 更新成 2。
- 6看片段 [4,6]:从位置 4 起跳能延伸到 6,比原来的 0 远,last[4] 更新成 6。
- 7看片段 [8,10]:从位置 8 起跳能延伸到 10,比原来的 0 远,last[8] 更新成 10。
- 8看片段 [1,9]:从位置 1 起跳能延伸到 9,比原来的 0 远,last[1] 更新成 9。
- 9看片段 [1,5]:位置 1 已经能到 9,比 5 更远或相等,last[1] 保持不变。
- 10看片段 [5,9]:从位置 5 起跳能延伸到 9,比原来的 0 远,last[5] 更新成 9。
- 11last 数组建好了。接下来从左往右扫,用 pre 记当前段右端(起步 0),mx 记这一段内能延伸到的最远。绿色格子代表已经覆盖到的位置。
- 12位置 0 能从某个起点延伸到 2,把这一段内的最远 mx 抬到 2。
- 13恰好走到当前段的右端 pre = 0,必须接一段新片段:ans 加到 1,把覆盖边界一口气推到这一段攒出的最远 2。
- 14位置 1 能从某个起点延伸到 9,把这一段内的最远 mx 抬到 9。
- 15位置 2 的起跳 last[2] = 0 到不了更远,mx 还是 9,继续往右走。
- 16恰好走到当前段的右端 pre = 2,必须接一段新片段:ans 加到 2,把覆盖边界一口气推到这一段攒出的最远 9。
- 17位置 3 的起跳 last[3] = 0 到不了更远,mx 还是 9,继续往右走。
- 18位置 4 的起跳 last[4] = 6 到不了更远,mx 还是 9,继续往右走。
- 19位置 5 的起跳 last[5] = 9 到不了更远,mx 还是 9,继续往右走。
- 20位置 6 的起跳 last[6] = 0 到不了更远,mx 还是 9,继续往右走。
- 21位置 7 的起跳 last[7] = 0 到不了更远,mx 还是 9,继续往右走。
- 22位置 8 能从某个起点延伸到 10,把这一段内的最远 mx 抬到 10。
- 23位置 9 的起跳 last[9] = 0 到不了更远,mx 还是 10,继续往右走。
- 24恰好走到当前段的右端 pre = 9,必须接一段新片段:ans 加到 3,把覆盖边界一口气推到这一段攒出的最远 10。
- 25扫完,覆盖边界 pre 已经到 10,盖住了整段 [0, 10]。一路接了 3 段,所以答案就是 3。
⚠️ 容易写错的地方
✗ 错:last 数组开成 time+1 长或下标越界
✓ 对:长度取 time,下标 0 到 time 减 1
起点 ≥ time 的片段对覆盖 [0, time] 无用,直接忽略即可
✗ 错:每个位置都给 ans 加一
✓ 对:只在 pre 等于 i 时加一
pre 之前都在用 max 攒下一跳最远 mx,走到段右端才落实一次跳跃
✗ 错:漏判无解
✓ 对:扫到 mx ≤ i 立刻返回 -1
说明从 ≤ i 的起点最远只能到 i,位置 i 到 i 加 1 出现断层
完整代码(Python / C++ / Java)
Python
from __future__ import annotations
from typing import *
from collections import *
from functools import *
from itertools import *
from math import *
from heapq import *
from bisect import *
class Solution:
def videoStitching(self, clips: List[List[int]], time: int) -> int:
last = [0] * time
for a, b in clips:
if a < time:
last[a] = max(last[a], b)
ans = mx = pre = 0
for i, v in enumerate(last):
mx = max(mx, v)
if mx <= i:
return -1
if pre == i:
ans += 1
pre = mx
return ansC++
#include <algorithm>
#include <array>
#include <cctype>
#include <climits>
#include <cmath>
#include <deque>
#include <functional>
#include <iostream>
#include <limits>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
using namespace std;
class Solution {
public:
int videoStitching(vector<vector<int>>& clips, int time) {
vector<int> last(time);
for (auto& v : clips) {
int a = v[0], b = v[1];
if (a < time) {
last[a] = max(last[a], b);
}
}
int mx = 0, ans = 0;
int pre = 0;
for (int i = 0; i < time; ++i) {
mx = max(mx, last[i]);
if (mx <= i) {
return -1;
}
if (pre == i) {
++ans;
pre = mx;
}
}
return ans;
}
};Java
import java.util.*;
class Solution {
public int videoStitching(int[][] clips, int time) {
int[] last = new int[time];
for (int[] e : clips) {
int a = e[0], b = e[1];
if (a < time) {
last[a] = Math.max(last[a], b);
}
}
int ans = 0, mx = 0, pre = 0;
for (int i = 0; i < time; ++i) {
mx = Math.max(mx, last[i]);
if (mx <= i) {
return -1;
}
if (pre == i) {
++ans;
pre = mx;
}
}
return ans;
}
}复杂度
时间
O(n + time)
建 last 表扫 n 个片段,再线性扫 time 个位置
空间
O(time)
一个长度 time 的 last 数组
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 视频拼接 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
这题和「跳跃游戏 II(LeetCode 45)」是一回事吗?+
本质同构。把 last[i] 看成站在位置 i 最远能跳到哪,求覆盖 [0, time] 的最少片段,就是求从 0 跳到 time 的最少步数。两题用同一套贪心:在当前这一跳能到的范围内预选下一跳能到的最远点,走到边界就跳一次、步数加一。
能不能用动态规划做?+
可以,但要按终点递增来算、别按输入片段顺序扫。设 dp[i] 为覆盖 [0, i] 的最少片段,dp[0]=0、其余初始为无穷;外层 for j 从 1 到 time,内层枚举所有满足 a < j ≤ b 的片段 [a,b],用 dp[a]+1 去更新 dp[j]。这样算 dp[j] 时它依赖的 dp[a](a < j)都已算好,不依赖输入片段的先后顺序;答案是 dp[time]。时间 O(time × n),比贪心慢一档,但更直观、好推广到带权变体。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄声线把 视频拼接 一步步讲透(全站 0 节语音精讲,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。