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<String> set;
    int r;
    int c;
    public Solution(int n_rows, int n_cols) {
        set = new HashSet<String>();
        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<Integer, Integer> 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<String>() {
            @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<strs.length;i++){
            String res = strs[i];
            for(int j = 0;j<strs.length;j++){
                if(i==j) continue;
                res = getDiff(res, strs[j]);
                //说明这个元素没有自己的子序列
                if("".equals(res)) break;
            }
            if(!"".equals(res)) return res.length();
        }
        return -1;
    }
    public String getDiff(String a,String b) {
        char[] aa = a.toCharArray();
        int idx = 0;
        for(char c:b.toCharArray()) {
            //这个元素有了继续往下对比
            if(c==aa[idx]) {
                //如果最后一个元素都比对完了说明这个序列不是唯一的。返回空
                if(idx==aa.length-1) return "";
                idx++;
            }
        }
        //到这还没返回说明这个子序列 b中没有,暂时算是特殊的
        return a;
    }
}

我简直呵呵了,性能百分百过的。我为什么会觉得这道题要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<nums.length-1;i++){
                if(nums[i] == nums[i+1] && nums[i] == 0) return true;
            }
            return false;
        }
        for(int i = 0;i<nums.length;i++) {
            int sum = nums[i];
            for(int j = i+1;j<nums.length;j++) {
                sum += nums[j];
                if(sum%k == 0) return true;
            }
        }
        return false;
    }
}

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

class Solution {
    public boolean checkSubarraySum(int[] nums, int k) {
        Map<Integer, Integer> 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<String> d) {
        String res = "";
        int max = 0;
        for(String dd:d) {
            if(isOk(s, dd)) {
                //是子序列,直接取最大的
                if(max<dd.length()) {
                    max = dd.length();
                    res = dd;
                }else if(max == dd.length()) {//相同长度取字典顺序最小的
                    res = dd.compareTo(res)<0?dd:res;
                }
            }
        }
        return res;
    }
    //判断是不是子序列
    public boolean isOk(String s,String d) {
        char[] c = d.toCharArray();
        int idx = 0;
        for(char ss : s.toCharArray()) {
            if(ss==c[idx]) {
                idx++;
                //d中所有元素s中都有,所以直接返回true
                if(idx == c.length) return true;
            }
        }
        return false;
    }
}

这个没什么好说的。就是暴力破解。用到了两个方法:一个是判断一个字符串是不是另一个字符串的子序列。还有就是字符串字典顺序比较。我这里直接调用的内置函数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<nums.length;i++){
            if(nums[i]==0) nums[i]=-1;
        }
        //找到和为0的最长子序列(和上一道题类似)
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        map.put(0,-1);//默认就是0了。
        int sum = 0;
        int max = 0;
        for(int i = 0;i<nums.length;i++){
            sum += nums[i];
            //当再次出现和是sum,说明中间累加这么多和为0.也就是1和-1数目相等
            if(map.containsKey(sum)) {
                max = Math.max(max, i-map.get(sum));
            }else {
                map.put(sum,i);//第一次出现和是sum的情况。
            }
        }
        return max;
    }

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

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存储。而这里用数组存储了。其实我觉得就是用空间换时间嘛。不是很惊喜。这道题就这样了。
这篇笔记就记到这里,如果稍微帮到你了记得点个喜欢点个关注。也祝大家工作顺顺利利!生活愉快!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,639评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,277评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,221评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,474评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,570评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,816评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,957评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,718评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,176评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,511评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,646评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,322评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,934评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,755评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,987评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,358评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,514评论 2 348