加载中...
955-删列造序 II(Delete Columns to Make Sorted II)
发表于:2021-12-03 | 分类: 中等
字数统计: 426 | 阅读时长: 2分钟 | 阅读量:

原文链接: https://leetcode-cn.com/problems/delete-columns-to-make-sorted-ii

英文原文

You are given an array of n strings strs, all of the same length.

We may choose any deletion indices, and we delete all the characters in those indices for each string.

For example, if we have strs = ["abcdef","uvwxyz"] and deletion indices {0, 2, 3}, then the final array after deletions is ["bef", "vyz"].

Suppose we chose a set of deletion indices answer such that after deletions, the final array has its elements in lexicographic order (i.e., strs[0] <= strs[1] <= strs[2] <= ... <= strs[n - 1]). Return the minimum possible value of answer.length.

 

Example 1:

Input: strs = ["ca","bb","ac"]
Output: 1
Explanation: 
After deleting the first column, strs = ["a", "b", "c"].
Now strs is in lexicographic order (ie. strs[0] <= strs[1] <= strs[2]).
We require at least 1 deletion since initially strs was not in lexicographic order, so the answer is 1.

Example 2:

Input: strs = ["xc","yb","za"]
Output: 0
Explanation: 
strs is already in lexicographic order, so we do not need to delete anything.
Note that the rows of strs are not necessarily in lexicographic order:
i.e., it is NOT necessarily true that (strs[0][0] <= strs[0][1] <= ...)

Example 3:

Input: strs = ["zyx","wvu","tsr"]
Output: 3
Explanation: We have to delete every column.

 

Constraints:

  • n == strs.length
  • 1 <= n <= 100
  • 1 <= strs[i].length <= 100
  • strs[i] consists of lowercase English letters.

中文题目

给定由 n 个字符串组成的数组 strs,其中每个字符串长度相等。

选取一个删除索引序列,对于 strs 中的每个字符串,删除对应每个索引处的字符。

比如,有 strs = ["abcdef", "uvwxyz"],删除索引序列 {0, 2, 3},删除后 strs["bef", "vyz"]

假设,我们选择了一组删除索引 answer,那么在执行删除操作之后,最终得到的数组的元素是按 字典序strs[0] <= strs[1] <= strs[2] ... <= strs[n - 1])排列的,然后请你返回 answer.length 的最小可能值。

 

示例 1:

输入:strs = ["ca","bb","ac"]
输出:1
解释: 
删除第一列后,strs = ["a", "b", "c"]。
现在 strs 中元素是按字典排列的 (即,strs[0] <= strs[1] <= strs[2])。
我们至少需要进行 1 次删除,因为最初 strs 不是按字典序排列的,所以答案是 1。

示例 2:

输入:strs = ["xc","yb","za"]
输出:0
解释:
strs 的列已经是按字典序排列了,所以我们不需要删除任何东西。
注意 strs 的行不需要按字典序排列。
也就是说,strs[0][0] <= strs[0][1] <= ... 不一定成立。

示例 3:

输入:strs = ["zyx","wvu","tsr"]
输出:3
解释:
我们必须删掉每一列。

 

提示:

  • n == strs.length
  • 1 <= n <= 100
  • 1 <= strs[i].length <= 100
  • strs[i] 由小写英文字母组成

通过代码

官方题解

方法 1:贪心

想法

针对该问题,我们考虑保留哪些列去获得最终的有序结果,而不是删除哪些列。

如果第一列不是字典序排列的,我们就必须删除它。

否则,我们需要讨论是否保留第一列。会出现以下两种情况:

  • 如果我们不保留第一列,则最后答案的行需要保证有序;

  • 如果我们保留了第一列,那么最终答案的行(除去第一列)只需要在第一个字母相同的情况下需要保证有序。

    这个描述很难理解,看下面的例子:

    假设我们有 A = ["axx", "ayy", "baa", "bbb", "bcc"],当我们保留第一列之后,最终行变成 R = ["xx", "yy", "aa", "bb", "cc"],对于这些行,并不要求所有有序(R[0] <= R[1] <= R[2] <= R[3] <= R[4]),只需要达到一个较弱的要求:对于第一个字母相同的行保证有序(R[0] <= R[1]R[2] <= R[3] <= R[4])。

现在,我们只将结论应用到第一列,但实际上这个结论对每列都合适。如果我们不能取用第一列,就删除它。否则,我们就取用第一列,因为无论如何都可以使要求更简单。

算法

首先没有任意列保留,对于每一列:如果保留后结果保持有序,就保留这一列;否则删除它。

[]
class Solution { public int minDeletionSize(String[] A) { int N = A.length; int W = A[0].length(); int ans = 0; // cur : all rows we have written // For example, with A = ["abc","def","ghi"] we might have // cur = ["ab", "de", "gh"]. String[] cur = new String[N]; for (int j = 0; j < W; ++j) { // cur2 : What we potentially can write, including the // newest column col = [A[i][j] for i] // Eg. if cur = ["ab","de","gh"] and col = ("c","f","i"), // then cur2 = ["abc","def","ghi"]. String[] cur2 = Arrays.copyOf(cur, N); for (int i = 0; i < N; ++i) cur2[i] += A[i].charAt(j); if (isSorted(cur2)) cur = cur2; else ans++; } return ans; } public boolean isSorted(String[] A) { for (int i = 0; i < A.length - 1; ++i) if (A[i].compareTo(A[i+1]) > 0) return false; return true; } }
[]
class Solution(object): def minDeletionSize(self, A): def is_sorted(A): return all(A[i] <= A[i+1] for i in xrange(len(A) - 1)) ans = 0 # cur : all rows we have written # For example, with A = ["abc","def","ghi"] we might have # cur = ["ab", "de", "gh"]. cur = [""] * len(A) for col in zip(*A): # cur2 : What we potentially can write, including the # newest column 'col'. # Eg. if cur = ["ab","de","gh"] and col = ("c","f","i"), # then cur2 = ["abc","def","ghi"]. cur2 = cur[:] for i, letter in enumerate(col): cur2[i] = cur2[i] + letter if is_sorted(cur2): cur = cur2 else: ans += 1 return ans

复杂度分析

  • 时间复杂度:$O(NW^2)$,其中 $N$ 是 A 的长度,$W$ 是 A[i] 的长度。
  • 空间复杂度:$O(NW)$。

方法 2:优化贪心

解释

方法 1 可以用更少的空间和时间。

核心思路是记录每一列的”割“信息。在第一个例子中,A = ["axx","ayy","baa","bbb","bcc"]R 也是相同的定义),第一列将条件 R[0] <= R[1] <= R[2] <= R[3] <= R[4] 切成了 R[0] <= R[1]R[2] <= R[3] <= R[4]。也就是说,"a" == column[1] != column[2] == "b" ”切割“了 R 中的一个条件。

从更高层面上说,我们的算法只需要考虑新加进的列是否保证有序。通过维护”割“的信息,只需要比较新列的字符。

[]
class Solution { public int minDeletionSize(String[] A) { int N = A.length; int W = A[0].length(); // cuts[j] is true : we don't need to check any new A[i][j] <= A[i][j+1] boolean[] cuts = new boolean[N-1]; int ans = 0; search: for (int j = 0; j < W; ++j) { // Evaluate whether we can keep this column for (int i = 0; i < N-1; ++i) if (!cuts[i] && A[i].charAt(j) > A[i+1].charAt(j)) { // Can't keep the column - delete and continue ans++; continue search; } // Update 'cuts' information for (int i = 0; i < N-1; ++i) if (A[i].charAt(j) < A[i+1].charAt(j)) cuts[i] = true; } return ans; } }
[]
class Solution(object): def minDeletionSize(self, A): # cuts[i] is True : we don't need to check col[i] <= col[i+1] cuts = [False] * (len(A) - 1) ans = 0 for col in zip(*A): if all(cuts[i] or col[i] <= col[i+1] for i in xrange(len(col) - 1)): for i in xrange(len(col) - 1): if col[i] < col[i+1]: cuts[i] = True else: ans += 1 return ans

复杂度分析

  • 时间复杂度:$O(NW)$,其中 $N$ 是 A 的长度,$W$ 是 A[i] 的长度。
  • 空间复杂度:额外空间开销 $O(N)$(在 Python 中,zip(*A) 需要 $O(NW)$ 的空间)。

统计信息

通过次数 提交次数 AC比率
3377 10161 33.2%

提交历史

提交时间 提交结果 执行时间 内存消耗 语言
上一篇:
953-验证外星语词典(Verifying an Alien Dictionary)
下一篇:
954-二倍数对数组(Array of Doubled Pairs)
本文目录
本文目录