LeetCode 960. 删列造序 III
题目描述


题意分析
每次删除一个列下标,就要从所有字符串中同时删除这一列。要求删除后每一行内部的字符都按非降序排列,求最少删除列数。等价于按原列顺序保留尽可能多的列,并让这同一组列在所有行中都非降序。
解法:列序 DP
核心思路
[!blue]
将一列看作由所有行字符组成的整体。对于原下标
i < j,只有每一行都满足strs[r][i] <= strs[r][j],列j才能接在列i后面。任意一行不满足,都不能把这两列作为相邻保留列。定义
dp[j]为以第j列结尾的最长合法保留列序列长度。单独保留这一列一定合法,所以所有状态初始为 1。枚举它之前的列i,若两列在所有行中都能衔接,就用dp[i] + 1更新dp[j]。这个转移只需检查最后两列:之前的序列在每行都已非降序,再有第
i列不大于第j列,根据传递性,整段接上j后仍非降序。反过来,任何以j结尾、长度大于 1 的合法序列都有某个倒数第二列i,必然会被上述枚举覆盖,因此能求到最优长度。最长序列可以在任意列结束,取所有
dp[j]的最大值best。总列数m减去最多可保留的best,就是最少删除数。
解题步骤
- 令每列的
dp为 1,best = 1。- 从左到右枚举结尾列
j,再枚举i = 0..j-1。- 逐行比较这两列;只要存在一行前字符更大,立即停止此次兼容性检查。
- 若所有行都通过,用
dp[i] + 1更新dp[j];处理完j后更新best。- 返回
m - best。只有一列时直接得到 0;相等字符也允许同时保留。
代码实现
class Solution {
public int minDeletionSize(String[] strs) {
int n = strs.length;
int m = strs[0].length();
int[] dp = new int[m];
Arrays.fill(dp, 1);
int best = 1;
for (int j = 0; j < m; j++) {
for (int i = 0; i < j; i++) {
boolean ok = true;
// 只有所有行都非降序,这两个列才能相接。
for (int r = 0; r < n; r++) {
if (strs[r].charAt(i) > strs[r].charAt(j)) {
ok = false;
break;
}
}
// 从较早的合法结尾扩展一列。
if (ok) {
dp[j] = Math.max(dp[j], dp[i] + 1);
}
}
best = Math.max(best, dp[j]);
}
return m - best;
}
}
func minDeletionSize(strs []string) int {
n := len(strs)
m := len(strs[0])
dp := make([]int, m)
for i := 0; i < m; i++ {
dp[i] = 1
}
best := 1
for j := 0; j < m; j++ {
for i := 0; i < j; i++ {
ok := true
// 只有所有行都非降序,这两个列才能相接。
for r := 0; r < n; r++ {
if strs[r][i] > strs[r][j] {
ok = false
break
}
}
// 从较早的合法结尾扩展一列。
if ok && dp[i]+1 > dp[j] {
dp[j] = dp[i] + 1
}
}
if dp[j] > best {
best = dp[j]
}
}
return m - best
}
复杂度分析
设有
n行、m列,与代码变量含义一致。
- 时间复杂度:$O(nm^2)$。枚举所有前后列对,每对最多比较
n行。- 空间复杂度:$O(m)$。动态规划数组为每一列保存一个最优长度。
关键点总结
[!green]
- 选择对象是整列,所有行共同约束同一条列序列。
dp[j]必须以j结尾,才能通过比较i、j判断是否可扩展。- 删除不改变剩余列的原顺序,列下标必须递增。
易错点总结
[!yellow]
- 不能只让某一行通过比较,必须所有行都满足非降序。
- 每行各自选不同的保留列,不符合共同删除同一列的操作。
- 非降序允许相等,冲突条件是
>,不是>=。- 不能只用最后一列的
dp,最优列序列可能在更早位置结束。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 300. 最长递增子序列 | 中等 | 可把每一列视为向量,只有前列在所有行都不大于后列时才能衔接,转为最长可保留列链。 |
| 944. 删列造序 | 简单 | 删列造序系列。I 可以逐列独立判断上下是否有序;III 必须考虑保留列之间的前后关系。 |
| 955. 删列造序 II | 中等 | 删列造序系列。II 按列维护相邻行的字典序关系,III 改为对列之间的兼容关系做 DP。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!