LeetCode Weekly Contest 26解题思路

来源:互联网 发布:mac outlook 导入规则 编辑:程序博客网 时间:2024/05/22 00:24

LeetCode Weekly Contest 26解题思路

赛题

本次周赛主要分为以下4道题:

  • 521 Longest Uncommon Subsequence I (3分)
  • 522 Longest Uncommon Subsequence II (6分)
  • 547 Friend Circles (7分)
  • 548 Split Array with Equal Sum (9分)

521 Longest Uncommon Subsequence I

Problem:

Given a group of two strings, you need to find the longest uncommon subsequence of this group of two strings. The longest uncommon subsequence is defined as the longest subsequence of one of these strings and this subsequence should not be any subsequence of the other strings.

A subsequence is a sequence that can be derived from one sequence by deleting some characters without changing the order of the remaining elements. Trivially, any string is a subsequence of itself and an empty string is a subsequence of any string.

The input will be two strings, and the output needs to be the length of the longest uncommon subsequence. If the longest uncommon subsequence doesn’t exist, return -1.

Example 1:

Input: “aba”, “cdc”
Output: 3
Explanation: The longest uncommon subsequence is “aba” (or “cdc”),
because “aba” is a subsequence of “aba”,
but not a subsequence of any other strings in the group of two strings.

Note:

1.Both strings’ lengths will not exceed 100.
2.Only letters from a ~ z will appear in input strings.

这道题的难点在于理解题目意思。。。

public int findLUSlength(String a, String b) {        int la = a.length();        int lb = b.length();        if(la != lb){            return la > lb ? la : lb;        }        else if(!a.equals(b)){            return la;        }        else            return -1;    }

简单叙述一下,当两个字符串的长度不一致时,最大长度的字符串一定是Longest Uncommon Subsequence,因为它的子集一定与长度较小的字符串不一致,其次如果当两个字符串相等时,如果相等则说明,它们都包含对方,无解,返回-1即可。

522 Longest Uncommon Subsequence II

Problems:

Given a list of strings, you need to find the longest uncommon subsequence among them. The longest uncommon subsequence is defined as the longest subsequence of one of these strings and this subsequence should not be any subsequence of the other strings.

A subsequence is a sequence that can be derived from one sequence by deleting some characters without changing the order of the remaining elements. Trivially, any string is a subsequence of itself and an empty string is a subsequence of any string.

he input will be a list of strings, and the output needs to be the length of the longest uncommon subsequence. If the longest uncommon subsequence doesn’t exist, return -1.

Example 1:

Input: “aba”,”cdc”,”eae”
Output: 3

Note:

1.All the given strings’ lengths will not exceed 10.
2.The length of the given list will be in the range of [2, 50].

上一道题的升级版,让你求字符串数组中的longest uncommon subsequence. 像这种题目,理解了最终目标之后,直接在脑海中构建最复杂的测试用例,比如,第一,每个字符串的长度不定,有大有小。第二,可以多次出现字符串长度相同的字符串。比如如下测试用例:

测试用例:
“a”,”b”,”c”,”dd”,”ef”,”sd”,”dd”,”aaab”,”aaac”,”dasfdaf”,”aaaassy”

刚拿到题目的时候,就直接上手去做了,导致最后的思路完全与实际AC解背离,自己的抽象能力不够。

这个问题可以分为如下:

  • 对于长度相同的字符串组的longest uncommon subsequence是什么?
  • 对于长度不一的字符串组的longest uncommon subsequence是什么?

字符串组长度相同
长度相同的字符串中,如果存在一个字符串不等于任何其他字符串时,那么,它的最长字符串长度为该字符串的长度。如“abc”,“cdf”,“abc”,它的解为3。

字符串组长度不一
这种情况更简单了,它的最长字符串长度一定是字符串长度最大的那个字符串的长度。如“abc”和“cdvd”,它的解为4。

上述情况混搭
这种情况是上述情况的最一般情况,此时我们可以分别求出长度相同的字符串组的longest uncommon subsequence,然和选择较大的那个,作为整体的longest uncommon subsequence。如:”ab”,”cd”,”aaa”,”aaa”,此时它的解为2。

好了,有了这些基本情况,我们就可以去求解该问题了,在此之前还需要定性的去比较它的时间复杂度,是O(n)还是O(n2)?如果对题解有了丰富的经验,基本上一眼能看出,而我刚开始做时,直接用了一个单层的for循环,导致的结果是各种复杂情况的讨论,尝试了很多次都没成功。

很明显,给你一个字符串“abv”,如何确保它在整个字符串数组中是没有公共子集的?一个唯一的办法就是比较所有元素,如果都不是唯一的那么“abv”显然是一个可能的候选解,而候选解还有剩下的所有元素,那么就应该两层循环。

所以有

public int findLUSlength(String[] strs) {        if(strs.length == 0) return -1;        int max = -1;        for (int i = 0; i < strs.length; i++){            boolean isLong = true;            for (int j = 0; j < strs.length; j++){                if (i == j || strs[j].length() < strs[i].length()) continue;                if (isSubSequnce(strs[j], strs[i])) {                    isLong = false;                    break;                }            }            if(isLong){                max = Math.max(max, strs[i].length());            }        }        return max;    }    private boolean isSubSequnce(String seq, String subSeq){ //没有先后顺序        if (seq.length() < subSeq.length()) {            String tmp = seq;            seq = subSeq;            subSeq = tmp;        }        int index_seq = 0;        int index_sub = 0;        while (index_seq < seq.length()){            if (seq.charAt(index_seq) == subSeq.charAt(index_sub)){                index_sub ++;            }            index_seq ++;            if(index_sub == subSeq.length())                return true;        }        return false;    }

这是我的AC解,主要思路就是两层循环,内循环是用来确定当前元素是否符合【不是其他任何字符串的subsequence】,其中要注意一个细节,必须忽略那些长度比当前元素小的那些字符串,因为我们的isSubsequence()方法是无序比较,只要满足在当前长度之上,才更新max。外循环则是用来更新max的,我们需要找到所有的uncommon subsequence中的最长的uncommon subsequence。

此处可以可以优化下isSubsequence(),如下

private boolean isSubSequnce(String seq, String subSeq){ //没有先后顺序        if (seq.length() < subSeq.length()) {            String tmp = seq;            seq = subSeq;            subSeq = tmp;        }        int j = 0;        for (int i = 0; i < seq.length(); i++){            if(seq.charAt(i) == subSeq.charAt(j)) j++;            if(j == subSeq.length()) return true;        }        return false;    }

这种结构更加短小精悍,后续的subSequence比较都用这个版本了。

547 Friend Circles

Problem:

There are N students in a class. Some of them are friends, while some are not. Their friendship is transitive in nature. For example, if A is a direct friend of B, and B is a direct friend of C, then A is an indirect friend of C. And we defined a friend circle is a group of students who are direct or indirect friends.

Given a N*N matrix M representing the friend relationship between students in the class. If M[i][j] = 1, then the ith and jth students are direct friends with each other, otherwise not. And you have to output the total number of friend circles among all the students.

Example 1:

Input:
[[1,1,0],
[1,1,0],
[0,0,1]]
Output: 2
Explanation:The 0th and 1st students are direct friends, so they are in a friend circle.
The 2nd student himself is in a friend circle. So return 2.

Example 2:

Input:
[[1,1,0],
[1,1,1],
[0,1,1]]
Output: 1
Explanation:The 0th and 1st students are direct friends, the 1st and 2nd students are direct friends,
so the 0th and 2nd students are indirect friends. All of them are in the same friend circle, so return 1.

Note:

1.N is in range [1,200].
2.M[i][i] = 1 for all students.
3.If M[i][j] = 1, then M[j][i] = 1.

这道题其实很简单,考的就是union的遍历,但我居然被union操作给折腾了,如果你能把它转换成一棵多叉树去理解,那么问题自然迎刃而解。

思路:
我朋友的朋友就是我朋友,它们属于同一类,所以我们只要把所有我的朋友和我朋友的朋友全部给删了,删的这部分属于一群人,那么剩下的就又回到了原始问题,同样的手段,直到所有的朋友圈被我们删完!所以它就是一个bfs的遍历+计数。

public int findCircleNum(int[][] M) {        if (M.length == 0) return 0;        int len = M.length;        int count = 0;        for (int i = 0; i < len; i++) {            if(M[i][i] != 0){                count ++;                bfs(M,i);            }        }        return count;    }    private void bfs(int[][] M,int row){        Queue<Integer> queue = new LinkedList<>();        queue.add(row);        while (!queue.isEmpty()){            int h = queue.poll();            for (int col = 0; col < M.length; col ++){                if(M[h][col] != 0){                    queue.offer(col);                    M[h][col] = 0;                }            }        }    }

没错,就是这么简单,然我是赛后才做出来的!!!总结一下,看到二维数组多半是bfs或者dfs,教训教训!

548 Split Array with Equal Sum

Problems:

Given an array with n integers, you need to find if there are triplets (i, j, k) which satisfies following conditions:

  • 0 < i, i + 1 < j, j + 1 < k < n - 1
  • Sum of subarrays (0, i - 1), (i + 1, j - 1), (j + 1, k - 1) and (k + 1, n - 1) should be equal.

where we define that subarray (L, R) represents a slice of the original array starting from the element indexed L to the element indexed R.

Example:

Input: [1,2,1,2,1,2,1]
Output: True
Explanation:
i = 1, j = 3, k = 5.
sum(0, i - 1) = sum(0, 0) = 1
sum(i + 1, j - 1) = sum(2, 2) = 1
sum(j + 1, k - 1) = sum(4, 4) = 1
sum(k + 1, n - 1) = sum(6, 6) = 1

Note:

1.1 <= n <= 2000.
2.Elements in the given array will be in range [-1,000,000, 1,000,000].

说说这道题的思路吧,刚开始拿到后,没什么思路,直接用暴力强心求解,结果可想而知,time limit了。代码如下

public boolean splitArray(int[] nums) {        if(nums.length <= 6) return false;        for (int i = 1; i + 4 < nums.length - 1; i++) {            for (int j = i + 2; j + 2 < nums.length - 1; j++) {                for (int k = j + 2; k < nums.length - 1; k++) {                    int sum1 = 0;                    int sum2 = 0;                    int sum3 = 0;                    int sum4 = 0;                    for (int l = 0; l < nums.length; l++) {                        if (l >= 0 && l <= i - 1)                            sum1 += nums[l];                        if (l >= i + 1 && l <= j - 1)                            sum2 += nums[l];                        if (l >= j + 1 && l <= k - 1)                            sum3 += nums[l];                        if (l >= k + 1 && l <= nums.length - 1)                            sum4 += nums[l];                    }                    if (sum1 == sum2 && sum3 == sum4 && sum2 == sum3)                        return true;                }            }        }        return false;    }

除了外面套了三层循环,里面还套了一层循环。但如果仔细观察代码的话,你会发现一个问题,如当i,j没有变动的情况下,而只有当k在变动时,其实sum(0,i-1)和sum(i+1,j-1)的值是不会发生变化的,但在上述代码中它又做了一次重复计算,所以可想它的运行时间。

我遇到这问题,直接想了一个比它更简单的问题,如给定一个数组,你从中间挖去一个i,使得左半部分的和和右半部分的和相等。同样的问题,你是否有答案了?

刚才已经发现一些问题了,主要就是sum一直不断重复的累加,这完全可以规避,比如:我们给定数组:
nums = [1,2,3,4,3,2,1]
sums = [1,3,6,10,13,15,16]
我们可以得到累加和,此时我们开始遍历i,只要使得sums[last]-nums[i] == 2 * (sums[i]-nums[i])就好了,这样做的好处是,所有的和只需要计算一遍就完事了。

于是,这道问题的思路也就有,我们先算出它的累加和,然后按照j,划分成左半部分和右半部分,此时,我们以同样的手段划分i,然后用一个set记录,下标i两边的sum值,然后继续划分k,如果在划分k的过程中,k的左半部分和右半部分相等且在set中有对应的值,我们就认为划分成功,如果全部遍历完毕,都没找到,则返回false。代码如下

public boolean splitArray(int[] nums) {        if(nums.length <= 6) return false;        int[] sum  = new int[nums.length];        sum[0] = nums[0];        for (int i = 1; i < nums.length; i++){            sum[i] =  sum[i-1]+nums[i];        }        for (int j = 3; j + 2 < nums.length -1; j++){            Set<Integer> set = new HashSet<>();            for (int i = 1; i < j -1; i++){                if ((sum[j-1]-nums[i]) == (sum[i]-nums[i]) * 2){                    set.add(sum[i-1]);                }            }            for (int k = j + 2; k < nums.length -1; k++){                if (sum[nums.length-1]-sum[j] - nums[k] == (sum[k]-nums[k]-sum[j]) * 2 && set.contains(sum[k]-nums[k]-sum[j]))                    return true;            }        }        return false;    }

这道题,我用了归简,把它转化成一个相对简单的问题,再进行扩展,从而AC。

0 0
原创粉丝点击