leetCode进阶算法题+解析(五十六)

最近工作还算是清闲,也不用加班什么的,所以有了大把时间刷题。直接开始吧。

随即翻转矩形

题目:题中给出一个 n_rows 行 n_cols 列的二维矩阵,且所有值被初始化为 0。要求编写一个 flip 函数,均匀随机的将矩阵中的 0 变为 1,并返回该值的位置下标 [row_id,col_id];同样编写一个 reset 函数,将所有的值都重新置为 0。尽量最少调用随机函数 Math.random(),并且优化时间和空间复杂度。
注意:
1 <= n_rows, n_cols <= 10000
0 <= row.id < n_rows 并且 0 <= col.id < n_cols
当矩阵中没有值为 0 时,不可以调用 flip 函数
调用 flip 和 reset 函数的次数加起来不会超过 1000 次

示例 1:
输入:
["Solution","flip","flip","flip","flip"]
[[2,3],[],[],[],[]]
输出: [null,[0,1],[1,2],[1,0],[1,1]]
示例 2:
输入:
["Solution","flip","flip","reset","flip"]
[[1,2],[],[],[],[]]
输出: [null,[0,0],[0,1],null,[0,0]]
输入语法解释:
输入包含两个列表:被调用的子程序和他们的参数。Solution 的构造函数有两个参数,分别为 n_rows 和 n_cols。flip 和 reset 没有参数,参数总会以列表形式给出,哪怕该列表为空

思路:这个题怎么说呢,我好想是没太看懂。而且这里说最少用random。最少就一次呗。先用我的理解去写写代码试试吧。这种题目本来就是开放的。
好了,第一版本代码出来了,我先贴代码:

class Solution {
    Set set;
    int r;
    int c;
    public Solution(int n_rows, int n_cols) {
        set = new HashSet();
        r = n_rows;
        c = n_cols;
    }
    
    public int[] flip() {
        int rr = new Random().nextInt(r);
        int cc = new Random().nextInt(c);
        while(set.contains(rr+","+cc)){
            rr = new Random().nextInt(r);
            cc = new Random().nextInt(c);
        }
        set.add(rr+","+cc);
        return new int[]{rr,cc};
    }
    
    public void reset() {
        set.clear();
    }
}

/**
 * Your Solution object will be instantiated and called as such:
 * Solution obj = new Solution(n_rows, n_cols);
 * int[] param_1 = obj.flip();
 * obj.reset();
 */

没啥技术含量,毕竟这种开放的题目对付实现就完事了。但是我估计肯定是有更合适的做法。我去看下题解吧。

class Solution {

    Map V = new HashMap<>();
    int nr, nc, rem;
    Random rand = new Random();

    public Solution(int n_rows, int n_cols) {
        nr = n_rows;
        nc = n_cols;
        rem = nr * nc;
    }

    public int[] flip() {
        int r = rand.nextInt(rem--);
        int x = V.getOrDefault(r, r);
        V.put(r, V.getOrDefault(rem, rem));
        return new int[]{x / nc, x % nc};
    }

    public void reset() {
        V.clear();
        rem = nr * nc;
    }
}

我还特意去百度代码中的一个方法:

getOrDefault(key,  defaultValue)

意味如果有这个key,则返回key对应的value。如果没有这个key,则返回给定的defaultValue.简单来说上面题目的思路是做Map映射,如果key不存在,则取并且从尾部取值对应到Key上,如果key存在,取对应的Value,并且把随机范围减一。这样做到用一维的map来维护了n*c个元素的二维数组。
这个题我写的挺无脑的,答案挺精巧的。总而言之是个挺有意思的题。学到的是一种新的思维方式。就是map映射做跳表的思路。。下一题。

最长特殊序列2

题目:给定字符串列表,你需要从它们中找出最长的特殊序列。最长特殊序列定义如下:该序列为某字符串独有的最长子序列(即不能是其他字符串的子序列)。子序列可以通过删去字符串中的某些字符实现,但不能改变剩余字符的相对顺序。空序列为所有字符串的子序列,任何字符串为其自身的子序列。输入将是一个字符串列表,输出是最长特殊序列的长度。如果最长特殊序列不存在,返回 -1 。

示例:
输入: "aba", "cdc", "eae"
输出: 3
提示:
所有给定的字符串长度不会超过 10 。
给定字符串列表的长度将在 [2, 50 ] 之间。

思路:这个题怎么说呢,本身序列的题就比较复杂。毕竟不是字面量,可以跳元素。你就看着限制条件。字符串长度不会超过十。总数小于50就能看出计算有多复杂。反正这种题目第一反应是dp没啥问题。至于状态转移方程我的慢慢去琢磨。大概的思路是每一个和其余的比找出特殊的序列。然后这些特殊的继续往下比,应该是个二维数组。我去试试吧。
算了,dp个屁,我还是先用自己的方式对付实现了再说吧。就暴力破解了。
直接贴代码:

class Solution {
    public int findLUSlength(String[] strs) {
        Arrays.sort(strs, new Comparator() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length()>o2.length()?-1:1;
            }
        });
        if(strs[0].length()>strs[1].length()) return strs[0].length();
        for(int i = 0;i

我简直呵呵了,性能百分百过的。我为什么会觉得这道题要dp?怕不是脑子进了水了。生生想了半个多小时到怀疑人生。
这个题简单的很,思路主要是:一个串中有特殊序列,那么这整个串一定是特殊的。有点心累,不想多说话,下一题吧。

连续的子序组合

题目:给定一个包含 非负数 的数组和一个目标 整数 k,编写一个函数来判断该数组是否含有连续的子数组,其大小至少为 2,且总和为 k 的倍数,即总和为 n*k,其中 n 也是一个整数。

示例 1:
输入:[23,2,4,6,7], k = 6
输出:True
解释:[2,4] 是一个大小为 2 的子数组,并且和为 6。
示例 2:
输入:[23,2,6,4,7], k = 6
输出:True
解释:[23,2,6,4,7]是大小为 5 的子数组,并且和为 42。
说明:
数组的长度不会超过 10,000 。
你可以认为所有数字总和在 32 位有符号整数范围内。

思路:这个题感觉最暴力的就是每一个数开始往后加。但是感觉暴力法会有问题。但是我要去试试。
第一版本暴力法解出来了,我先贴代码:

class Solution {
    public boolean checkSubarraySum(int[] nums, int k) {
        if(k==0) {
            for(int i=0;i

怎么说呢,现在越来越懒得写题解了,感觉有的题目比较简单,没啥好说的,太难的又说不明白。反正这道题面向测试案例编程。给出的结果中0,0 k=0的话也是true。所以有了第一个key=0的特殊处理。性能超过百分之五十。估计是有更好的做法。但是我没精力想了,直接去看性能排行第一的代码:

class Solution {
    public boolean checkSubarraySum(int[] nums, int k) {
        Map map = new HashMap<>();
        map.put(0, -1);
        int sum = 0, n = nums.length;
        for (int i = 0; i < n; i++) {
            sum += nums[i];
            int v = k != 0 ? sum % k : sum == 0 ? 0 : sum;
            Integer o = map.put(v, i);
            if (o != null) {
                if (i - o > 1) return true;
                map.put(v, o);
            }
        }
        return false;
    }
}

这个题的题解我觉得超级赞的一个思路。但是人家官方的说法太不好理解了。我自己的想法就是:

sum%k = n.   (sum+(x+y+z+...))%k =n。

我是不是可以理解x+y+z+...本身被k整除了?只要确定这个x.y,z..的数量大于2,就说明出现了能大于2的k的倍数(这个题坑的一点是哪怕1,-1或者0,0这种也算正确答案。)反正数学我也就这样了,什么取模对数啥的也心有余力不足了。用自己的方式能理解就挺好了。下一题。

通过删除字母匹配到字典里最长单词

题目:给定一个字符串和一个字符串字典,找到字典里面最长的字符串,该字符串可以通过删除给定字符串的某些字符来得到。如果答案不止一个,返回长度最长且字典顺序最小的字符串。如果答案不存在,则返回空字符串。

示例 1:
输入:
s = "abpcplea", d = ["ale","apple","monkey","plea"]
输出:
"apple"
示例 2:
输入:
s = "abpcplea", d = ["a","b","c"]
输出:
"a"
说明:
所有输入的字符串只包含小写字母。
字典的大小不会超过 1000。
所有输入的字符串长度不会超过 1000。

思路:第一反应就是判断这个单词是不是给定字符串的子序列。其实也就是暴力遍历法。找到最长的那个子序列。相同长度取排位靠前的。就是这样,我去试试能不能过。
翻车的地方莫名其妙。字典顺序最小的字符串。我本来以为是在数组中排名靠前的呢。我去把这一块改了
贴代码:

class Solution {
    public String findLongestWord(String s, List d) {
        String res = "";
        int max = 0;
        for(String dd:d) {
            if(isOk(s, dd)) {
                //是子序列,直接取最大的
                if(max

这个没什么好说的。就是暴力破解。用到了两个方法:一个是判断一个字符串是不是另一个字符串的子序列。还有就是字符串字典顺序比较。我这里直接调用的内置函数compareTo。想自己写就拆开比较就ok了,也没啥难度就是比较墨迹、这个题就这样了,下一题。

连续数组

题目:给定一个二进制数组, 找到含有相同数量的 0 和 1 的最长连续子数组(的长度)。

示例 1:
输入: [0,1]
输出: 2
说明: [0, 1] 是具有相同数量0和1的最长连续子数组。
示例 2:
输入: [0,1,0]
输出: 2
说明: [0, 1] (或 [1, 0]) 是具有相同数量0和1的最长连续子数组。
注意: 给定的二进制数组的长度不会超过50000。

思路:不知道为啥这个题没有任何时间空间的约束。反正暴力法就是数组最长才5w。我的想法就是建立一个list(因为不定长所以不用数组了)。然后0的出现个数统计,1的出现个数统计。就这样往下统计下去。最后遍历list找到连续两个数的较小的值中的最大值。说起来比较绕,但是我觉得实现起来还是很容易的。我去试试了。
好吧,是我没理解题意。这里0,1,0,1我以为结果是2.但是没想到这样是4,所以上面的思路推翻重试。暴力法就不合适了。我想想怎么实现吧。
我觉得我思路贼精巧,还好这两道题挨在一起了所以找到了思路。但是性能不咋地。我先附上代码:

class Solution {
    public int findMaxLength(int[] nums) {
        for(int i = 0;i map = new HashMap();
        map.put(0,-1);//默认就是0了。
        int sum = 0;
        int max = 0;
        for(int i = 0;i

上一道题也是这个思路,我觉得我注释写的比较清楚了。真的这两道题安排在一起帮了我。我再去看看性能排行第一的代码:

class Solution {
    public int findMaxLength(int[] nums) {
        if (null == nums || nums.length < 2) {
            return 0;
        }

        int length = nums.length;
        int mappingLength = 2 * length + 1;
        int[] mapping = new int[mappingLength];
        for (int i = 0; i < mappingLength; i++) {
            mapping[i] = -2;
        }
        mapping[length] = -1;

        int maxLength = 0;
        int count = 0;
        for (int i = 0; i < length; i++) {
            count += 2 * nums[i] - 1;
            if (mapping[count + length] >= -1) {
                maxLength = Math.max(maxLength, i - mapping[count + length]);
            } else {
                mapping[count + length] = i;
            }
        }
        return maxLength;
    }
}

本质上和我的思路是一样的。不过我把0赋值为-1了。而这里用的是2 * nums[i] - 1、算出来还是1就+1.0就-1。
区别就是我用的map存储。而这里用数组存储了。其实我觉得就是用空间换时间嘛。不是很惊喜。这道题就这样了。
这篇笔记就记到这里,如果稍微帮到你了记得点个喜欢点个关注。也祝大家工作顺顺利利!生活愉快!

你可能感兴趣的:(leetCode进阶算法题+解析(五十六))