题目描述

✅ 944. 删列造序

image-20260929105324403

image-20260929105324759

题意分析

把等长字符串看成字符网格,删除尽可能少的列,使每个保留列从上到下都非递减。要求检查的是每一列内部的上下顺序,不是各行字符串之间的字典序。

解法:逐列扫描计数

核心思路

[!blue]

删除其他列,不会改变当前列的字符及其上下位置。因此各列可以独立决定去留:一列原本有序就能保留,一列原本存在下降就必须删除,无法靠删除别的列修复。

判断一列是否有序,只需比较相邻两行。若每对都满足前者不大于后者,由大小关系的传递性,整列就是非递减的;若发现 strs[i][col] > strs[i + 1][col],这一列便不合法。

统计所有不合法的列,就是最少删除数:这些列每一列都必须删,删掉它们后剩余列又全部符合要求。发现一列的第一处下降后即可停止检查该列,因为后面无论还有多少处下降,都只需要删除这一列一次。

解题步骤

  1. 外层依次枚举每一列,删除数初始为 0。
  2. 对当前列,从上到下比较相邻两行的字符。
  3. 一旦前一行字符大于后一行,删除数加一,并跳出当前列的扫描。
  4. 检查完所有列后返回删除数。只有一行时,没有需要比较的相邻行,每列都能保留。

代码实现

class Solution {
    public int minDeletionSize(String[] strs) {
        int n = strs.length;
        int m = strs[0].length();
        int answer = 0;

        for (int col = 0; col < m; col++) {
            for (int i = 0; i < n - 1; i++) {
                if (strs[i].charAt(col) > strs[i + 1].charAt(col)) {
                    // 该列已确定必须删除,同列后续逆序不再重复计数。
                    answer++;
                    break;
                }
            }
        }

        return answer;
    }
}
func minDeletionSize(strs []string) int {
    n := len(strs)
    m := len(strs[0])
    answer := 0

    for col := 0; col < m; col++ {
        for i := 0; i < n-1; i++ {
            if strs[i][col] > strs[i+1][col] {
                // 该列已确定必须删除,同列后续逆序不再重复计数。
                answer++
                break
            }
        }
    }
    return answer
}

复杂度分析

  • 时间复杂度:$O(nm)$,n 为行数、m 为列数。
  • 空间复杂度:$O(1)$。

关键点总结

[!green]

  • 每列的合法性互不影响,不合法列的数量同时给出下界和可行删除方案。
  • 相邻比较全部通过,就足以保证整列非递减。
  • 统计单位是列,同一列最多增加一次答案。

易错点总结

[!yellow]

  • 非递减允许相等,只有严格大于下一行字符时才需要删除。
  • 发现下降后仍继续累加,会把同一列重复计数。
  • 只比较首尾字符,无法发现中间的局部下降。
  • 按行比较字符或整行字典序,会改变题目要求的检查方向。

相似题目

题目 难度 关联与区别
955. 删列造序 II 中等 本题各列独立检查上下是否有序,原题要求所有行整体字典序有序,列之间存在决策依赖。
960. 删列造序 III 困难 删列造序系列。I 要求每列从上到下有序,III 要求每行从左到右有序,需要选择最长兼容列子序列。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2023/18093535
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!