• [LeetCode 779.] K-th Symbol in Grammar


    LeetCode 779. K-th Symbol in Grammar

    一道找规律的题。

    题目描述

    On the first row, we write a 0. Now in every subsequent row, we look at the previous row and replace each occurrence of 0 with 01, and each occurrence of 1 with 10.

    Given row N and index K, return the K-th indexed symbol in row N. (The values of K are 1-indexed.) (1 indexed).

    Examples:
    Input: N = 1, K = 1
    Output: 0

    Input: N = 2, K = 1
    Output: 0

    Input: N = 2, K = 2
    Output: 1

    Input: N = 4, K = 5
    Output: 1

    Explanation:
    row 1: 0
    row 2: 01
    row 3: 0110
    row 4: 01101001

    Note:

    1. N will be an integer in the range [1, 30].
    2. K will be an integer in the range [1, 2^(N-1)].

    解题思路

    显然是要找规律,四行可能还看不清楚,多写几行就会发现:每一行的前半部分都和前一行的数相同,每一行的后半部分都是前半部分按位取反。

    这里我们最直观的办法就是按照规律来生成数据,然后查找对应位就行了,反正 N 最大也才 30 嘛。结果就是打脸,超时了。

    然后我们更进一步,不生成数据,只研究所需数据的生成路径,可以发现:

    • 如果该数字位于当前行的后半部分,那么只需要知道前半部分对应位置的数,取反即可;
    • 如果该数字位于当前行的前半部分,那么可以砍掉后半部分,继续操作(相当于跳入上一行);
    • 如果已经到达位置0,结束任务。

    最后我们得到的就是从位置0到达所求位置的路径,只需要知道取反操作的次数,就能够知道数字到底是0还是1。

    这个算法更快,一方面在于减少了内存读写、消除了大量冗余求值,另一方面是砍半操作压缩了求值路径,使得我们可以用最短的路径到达所需位置。

    参考解法

    /*
     * @lc app=leetcode id=779 lang=cpp
     *
     * [779] K-th Symbol in Grammar
     */
    
    // @lc code=start
    class Solution {
    public:
    /*
        int kthGrammar(int N, int K) {
            int sz = 1 << (N - 1);
            uint64_t *bits = new uint64_t[(sz+63)/64];
        #define SET0(bits, i) 
            do{ bits[(i)>>6] &= ~(1UL << ((i)&63)); }while(0)
        #define SET1(bits, i) 
            do{ bits[(i)>>6] |= (1UL << ((i)&63)); }while(0)
        #define GET(bits, i) 
            ((bits[(i)>>6] >> ((i)&63)) & 1)
    
            SET0(bits, 0);
            int len = 1;
            while(len < K) {
                for (int j=0; j<len; j++) {
                    if (GET(bits, j)) SET0(bits, len+j);
                    else SET1(bits, len+j);
                }
                len <<= 1;
            } // 规律,以2的幂次为单位,后半部分是前半部分按位取反
            // 每一行都是下一行的前半部分
            for (int i=0; i<len; i++) printf("%d", GET(bits,i));
            return GET(bits, K-1);
        } // TLE, case 30 434991989
    */
        int kthGrammar(int N, int K) {
            int res = 0;
            int i = K-1;
            int width = 1;
            while (width <= i) {
                width <<= 1;
            }
            while (i) {
                if (i >= width / 2) {
                    i = i - width / 2;
                    res = 1 - res;
                } // 后半部分的值是前半部分对应位置值取反
                width /= 2; // 折半降行
            }
            return res;
        } // AC
    };
    // @lc code=end
    
  • 相关阅读:
    「字符串算法」第4章 字典树课堂过关
    「字符串算法」第3章 KMP 算法课堂过关
    「字符串算法」第2章 Hash 和 Hash 表课堂过关
    「基础算法」第5章 广度搜索课堂过关
    「基础算法」第3章 二分算法课堂过关
    「基础算法」第1章 递推算法强化训练
    「基础算法」第1章 递推算法课堂过关
    YbtOJ:冲刺 NOIP2020 模拟赛 Day10
    【模板】轻重链剖分
    LINUX-磁盘空间
  • 原文地址:https://www.cnblogs.com/zhcpku/p/14697494.html
Copyright © 2020-2023  润新知