Leetcode Weekly Contest 101

这次比赛,网站挂了10多分钟。后来终于进去了。
我做出来3道,最后一道知道是DP,最后一步没当场分析出来。最后排名132/4300

900. RLE Iterator

https://leetcode.com/contest/weekly-contest-101/problems/rle-iterator/
这道题目就是,2个数字为一组,告诉你有几个几。
然后NEXT,就根据翻译出来的数来取。
比如【3,1】就是 【1,1,1】
那么肯定就是维护一个REST变量,和一个指针P,表明遍历到目前为止,指的这个数还剩几个没遍历。
比如上面的例子。我调用next(2) 那就是 P指向1,REST 为1. 还有1和1没用。

int[] A;
    int p;
    int rest = 0;
    public RLEIterator(int[] A) {
        this.A = A;
        p = -1;
    }
    
    public int next(int n) {
        while(rest - n < 0){
            n -= rest;
            p+=2;
            if(p>=A.length) return -1;
            rest = A[p-1];
        }
        rest -= n;
        if(p>=A.length) return -1;
        return A[p];
    }

901. Online Stock Span

https://leetcode.com/problems/online-stock-span/description/
这是道要维护前面所有小于等于当前值的题。那么看数据规模还要求O N
果断,知道每一步要么是O 1 要么是 Log N。
因为是维护前面连续的数,所以2分的概率不大。
一旦一个数大于栈顶元素,那么就可以把连续给断开。
比如 60,60,70,60. 最后一个60因为被70阻隔开了,所以只能返回1,而不是3.
根据这个性质。我们可以想到单调栈。如果一个数大于栈顶,其实那些小于这个数就可以POP了,因为也用不到了。

Stack<int[]> st = new Stack<>();
    int idx = 0;
    public StockSpanner() {
        st.push(new int[]{10001,-1});
    }
    
    public int next(int price) {
        while(st.peek()[0]<=price){
            st.pop();
        }
        int res = idx-st.peek()[1];
        st.push(new int[]{price,idx++});
        return res;
    }

902. Numbers At Most N Given Digit Set

https://leetcode.com/problems/numbers-at-most-n-given-digit-set/description/
这道题也不是太难。我的解法是先用公式把所有位数比N小的,求掉。
比如1,2,3. 3个数。 N=100,位数为3. 我们先把位数为1,和位数为2的求了。
位数为1的是3. 位数为2的个数是33.
再在位数和N相同的情况下,一位一位求。
位数相同
我们先看第一位能用几个。1,2,3 ; N=200 第一位是2.
我们先把第一位是比2小的数找到K 找到 然后后面2位,就可以随意了。K
3^(n-1)
下面看这个2是不是和能用的数相等。试的话,我们接着去求下一位,公式一样。
如果是1,3,5; N=200; 我们发现是3》2; 这个情况就可以直接跳出循环了。

public int atMostNGivenDigitSet(String[] D, int N) {
        N++;
        int l = D.length;
        int nl = (N+"").length();
        //cnt digit number < nl
        int cnt = 0;
        for(int i = 1; i < nl; i++){
            cnt += (int)Math.pow(l,i);
        }
        //cnt digit number = nl
        int k = nl-1;
        for(char c : (N+"").toCharArray()){
            int i = 0;
            for(; i < l && D[i].charAt(0) < c ; i++){}
            cnt += (i*(Math.pow(l,k)));
            if(i<l && D[i].charAt(0) == c){
                k--;
            }else{
                break;
            }
        }
        return cnt;
    }

最后我发现,如果正好是每一位都相等,那会漏掉完全相等的这个数。所以一开始用N++。
这样就解决了。

903. Valid Permutations for DI Sequence

https://leetcode.com/problems/valid-permutations-for-di-sequence/description/
这道题的思路是这样的。
我们先从DI来分析。
D 势必为1,0

如果之后是I。我们可以列举以J为结尾的上升时什么。 首先以0为结尾是不可能的。
其次,以1为结尾是可以的201. 以2为结尾也是可以的 是102
我们不妨定义(DP I J) 就是 I个数字的全排列里,符合到PATTERN.substring(0,i-1)的最后一个数字为J的个数有几个。
根据题目的例子
DID 的情况
(1, 0, 3, 2)
(2, 0, 3, 1)
(2, 1, 3, 0)
(3, 0, 2, 1)
(3, 1, 2, 0)
dp[4][0] = 2
dp[4][1] = 2
dp[4][2] = 1
dp[4][3] = 0
dp[4][4] = 0
如果我们要在DID 后面多加一个D,会怎么转变呢
如果要符合条件,dp[5][0] = {(2+1, 1+1, 3+1, 0+1,0),(2+1, 1+1, 3+1, 0+1,0)}(这里可以得知dp[5][0] = 0+dp[4][0])
dp[5][0] = {(2+1, 0+1, 3+1, 1+1,0),(3+1, 0+1, 2+1, 1+1,0)} (这里可以得知dp[5][0] = dp[4][0]+dp[4][1])
dp[5][0] = {(1, 0+1, 3+1, 2+1,0)} (这里可以得知dp[5][0] =dp[4][0]+dp[4][1]+dp[4][2])
那么当D的时候,dp[i][j] 可以从 dp[i-1][k] (k from j ... i-1)
同样可以发现dp[i][j] 在最后一个是字母是I的时候,我们可以从dp[i-1][k](k from 0...j-1)
那么比如DIDI的情况
dp[5][0] = 0 因为J-1《0
dp[5][1] = {(2+1, 1+1, 3+1, 0,1),(3+1, 1+1, 2+1, 0,1)} dp[5][1] = dp[4][0]
dp[5][2] = {(2+1, 1, 3+1, 0,2),(3+1, 1, 2+1, 0,2)} + {(2+1, 0, 3+1, 1,2),(2+1, 0, 3+1, 1,2)} dp[5][2] = dp[4][0]+dp[4][1]
综上

public int numPermsDISequence(String s) {
        int n = s.length()+1;
        long[][] dp = new long[n][n];
        dp[0][0] = 1;
        int M = 1000000007;
        for(int i = 1; i < n; i++){
            for(int j = 0; j <= i; j++){
                if(s.charAt(i-1) == 'D'){
                    for(int k = j; k < i; k++)
                        dp[i][j] = (dp[i][j]+dp[i-1][k])%M;
                }else{
                    for(int k = 0; k < j; k++)
                        dp[i][j] = (dp[i][j]+dp[i-1][k])%M;
                }
            }
        }
        long ans = 0;
        for(int i = 0; i < n; i++) ans = (ans+dp[n-1][i])%M;
        return (int) ans;
    }

下面我们思考可不可以优化,因为我看到最后生成答案有一个累加的操作。我就思考了这么一个问题,我可以不可以让DP状态就表示为(DP I J) 就是 I个数字的全排列里,符合到PATTERN.substring(0,i-1)的最后一个数字至多为J的个数有几个。
这样我最后就可以直接范围DP[N-1][N-1]就是解了。

随后我依然把这个解给摆出来。
DI 的情况有
(2,0,1)dp[3][1] = 1
(1,0,2)dp[3][2] = dp[3][1]+1 = 2;
dp[3][3] = dp[3][2]+0=2;
如果后面接了D
dp[4][0] 当最后一个数是0的话,那么就意味着,之前3,3的所有可能都可以组成新的解。因为只要在3的位置,所有的解,最后补上0,前面+1,都是WORK的。
dp[4][1] ,肯定是包含DP[4][0]的,如果最后一位需要是1. (3,0,2,1)(2,0,3,1)也是可以的
目前还看不出规律。
dp[4][2] = dp[4][1] + 最后一位需要是2的,这时我们会发现 (2,0,1,2) 是没法通过加1,使得最后一位是2,同时保持下降的。
所以我们需要排除dp[3][1]这个解,而这个1,是比当前的2小1 的。
我们猜想公式为dp[i][j] = dp[i][j-1]+dp[i-1][i-1]-dp[i-1][j-1]
这个递推公式的含义就是,少这个位数,如果要让最后一位是J,必须前一位至少也需要是J,所以J-1的都不行。

下面再看I的情况,我们可以得知,最后一位是0,是不可能存在I的。那么DP[4][1]。我们就可以加上DP[3][0]的那些解,最后补上1. 同理DP[4][2] 所有DP[3][1]的解都可以再最后补上2.
我们猜想公式为dp[i][j] = dp[i][j-1]+dp[i-1][j-1]

最后处理下边界情况。得到代码如下,时间复杂度被优化到N ^2

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

推荐阅读更多精彩内容

  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 12,723评论 0 33
  • <center>#1 Two Sum</center> link Description:Given an arr...
    铛铛铛clark阅读 2,137评论 0 3
  • 1.Sort Colors[Sort Colors]https://leetcode.com/problems/s...
    Zake_Wang阅读 413评论 0 1
  • 厦门回来那天就发现小仓鼠死了。试着把他救活,我以为救活了,然而最终还是没有。 和笼子一起扔到垃圾桶。 都过去三天了...
    lichangan阅读 364评论 0 0
  • 受伤的射手像今夜的戈壁——在黑暗中盼望仙人掌开出金黄的花朵。 硕大的花蕊变成他的弓箭,甘甜的浆果变成他的酒杯。 天...
    他是鬼彻阅读 195评论 0 2