1. 程式人生 > >[LeetCode] K-th Symbol in Grammar 語法中的第K個符號

[LeetCode] K-th Symbol in Grammar 語法中的第K個符號

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)].

這道題說第一行寫上了一個0,然後從第二行開始,遇到0,就變為01,遇到1,則變為10,問我們第N行的第K個數字是啥。這是一道蠻有意思的題目,首先如果沒啥思路的話,按照給定的方法,一行行generate出來,直到生成第N行,那麼第K個數字也就知道了。但是這種brute force的方法無法通過OJ,這裡就不多說了,需要想一些更高階的解法。我們想啊,遇到0變為01,那麼可不可以把0和1看作上一層0的左右子結點呢,同時,把1和0看作上一層1的左右子結點,這樣的話,我們整個結構就可以轉為二叉樹了,那麼前四層的二叉樹結構如下所示:

              0
       /             \
      0               1
   /     \         /     \
  0       1       1       0
 / \     / \     / \     / \
0   1   1   0   1   0   0   1

我們仔細觀察上面這棵二叉樹,第四層K=3的那個紅色的左子結點,其父結點的位置是第三層的第 (K+1)/2 = 2個紅色結點,而第四層K=6的那個藍色幽子結點,其父節點的位置是第三層的第 K/2 = 3個藍色結點。那麼我們就可以一層一層的往上推,直到到達第一層的那個0。所以我們的思路是根據當前層K的奇偶性來確定上一層中父節點的位置,然後繼續往上一層推,直到推倒第一層的0,然後再返回確定路徑上每一個位置的值,這天然就是遞迴的執行機制啊。我們可以根據K的奇偶性知道其是左結點還是右結點,由於K是從1開始的,所以當K是奇數時,其是左結點,當K是偶數時,其是右結點。而且還能觀察出來的是,左子結點和其父節點的值相同,右子結點和其父節點值相反,這是因為0換成了01,1換成了10,左子結點保持不變,右子結點flip了一下。想通了這些,那麼我們的遞迴解法就不難寫出來了,參見程式碼如下:

解法一:

class Solution {
public:
    int kthGrammar(int N, int K) {
        if (N == 1) return 0;
        if (K % 2 == 0) return (kthGrammar(N - 1, K / 2) == 0) ? 1 : 0;
        else return (kthGrammar(N - 1, (K + 1) / 2) == 0) ? 0 : 1;
    }
};

我們可以簡化下上面的解法,你們可能會說,納尼?已經三行了還要簡化?沒錯,博主就是這樣一個精益求精的人(此處應有掌聲