Leetcode 955. Delete Columns to Make Sorted II
Problem:
We are given an array A
of N
lowercase letter strings, all of the same length.
Now, we may choose any set of deletion indices, and for each string, we delete all the characters in those indices.
For example, if we have an array A = ["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 D
such that after deletions, the final array has its elements in lexicographic order (A[0] <= A[1] <= A[2] ... <= A[A.length - 1]
).
Return the minimum possible value of D.length
.
Example 1:
Input: ["ca","bb","ac"]
Output: 1
Explanation:
After deleting the first column, A = ["a", "b", "c"].
Now A is in lexicographic order (ie. A[0] <= A[1] <= A[2]).
We require at least 1 deletion since initially A was not in lexicographic order, so the answer is 1.
Example 2:
Input: ["xc","yb","za"]
Output: 0
Explanation:
A is already in lexicographic order, so we don't need to delete anything.
Note that the rows of A are not necessarily in lexicographic order:
ie. it is NOT necessarily true that (A[0][0] <= A[0][1] <= ...)
Example 3:
Input: ["zyx","wvu","tsr"]
Output: 3
Explanation:
We have to delete every column.
Note:
1 <= A.length <= 100
1 <= A[i].length <= 100
Solution:
这道题给我们一个字符串数组,要求删除每个字符串的若干列,使得剩下的字符串字典序列递增,这道题的第一想法就是贪心算法,对于每一列,判断如果不删除是否会导致字典序递减,如果是则删除,否则添加到result数组的末尾,最后result中存储的结果即删除若干列后的每个字符串的结果,代码如下
1 class Solution { 2 public: 3 int minDeletionSize(vector<string>& A) { 4 int m = A.size(); 5 if(m == 0) return 0; 6 int n = A[0].size(); 7 if(n == 0) return 0; 8 vector<string> result(m,""); 9 for(int j = 0;j != n;++j){ 10 bool deletion = false; 11 vector<string> add = result; 12 add[0] += A[0][j]; 13 for(int i = 1;i != m;++i){ 14 add[i] += A[i][j]; 15 if(add[i] < add[i-1]){ 16 deletion = true; 17 break; 18 } 19 } 20 if(!deletion) 21 result = add; 22 } 23 return n-result[0].size(); 24 } 25 };
然而这种解法效率不高,看起来时间复杂度是O(NW),N为数组长度,W为字符串长度,但仔细观察发现,在第15行,虽然只是简单的判断语句,但其实判断过程中又进行了一次循环,所以真正的时间复杂度为O(NW2)。因此我们需要想一种方法避免字符串的判断,如果能够从最后一个字符判断是否删除就好了。按照这个思路,我们用一个sorted数组,对于第j列而言,sorted[i]的意思表示A[i][j]和A[i+1][j]的值是否相同。对于每一列而言,如果存在A[i][j]大于A[i][j+1]的话,那这一列绝对要删除,所以sorted数组根本不用更新;如果A[i][j]小于A[i][j+1]的话,则说明这两个字符串必然满足字典序,则sorted[i]更新时为true;但是若是等于,则是否应该删除这一列取决于后面的字符串是否满足字典序,所以更新sorted数组之前应该对等于这一特殊情况进行处理。
举个例子就好理解了,比如"acb"和"abc“两个字符串,sorted初始化为{false}
第一次循环,a==a,因此sorted为false
第二次循环,sorted[0]为false且c>b,所以可知第二列必须要删除,既然第二列删除了,也就不需要更新sorted数组了
第三次循环,不需要删除,sorted[0]更新为true,输出结果为1
这道题要AC不难,不过要想出这种解法我也是第一时间没想出来,所以记录一下。
Code:
1 class Solution { 2 public: 3 int minDeletionSize(vector<string>& A) { 4 int m = A.size(); 5 if(m == 0) return 0; 6 int n = A[0].size(); 7 if(n == 0) return 0; 8 int result = 0; 9 vector<bool> sorted(m-1,false); 10 for(int j = 0;j != n;++j){ 11 bool deletion = false; 12 for(int i = 0;i != m-1;++i){ 13 if(!sorted[i] && A[i][j] > A[i+1][j]){ 14 deletion = true; 15 result++; 16 break; 17 } 18 } 19 if(deletion) continue; 20 for(int i = 0;i != sorted.size();++i){ 21 if(!sorted[i] && A[i][j] < A[i+1][j]){ 22 sorted[i] = true; 23 } 24 } 25 } 26 return result; 27 } 28 };