From bfd6728b9ad2ed8d8be690fbf7cb9321ad5ea014 Mon Sep 17 00:00:00 2001 From: nittoco <166355467+nittoco@users.noreply.github.com> Date: Sat, 20 Jul 2024 17:48:03 +0900 Subject: [PATCH] 779. K-th Symbol in Grammer https://leetcode.com/problems/k-th-symbol-in-grammar/description/ We build a table of n rows (1-indexed). We start by writing 0 in the 1st row. 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. For example, for n = 3, the 1st row is 0, the 2nd row is 01, and the 3rd row is 0110. Given two integer n and k, return the kth (1-indexed) symbol in the nth row of a table of n rows. --- 779. K-th Symbol in Grammer.md | 99 ++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 779. K-th Symbol in Grammer.md diff --git a/779. K-th Symbol in Grammer.md b/779. K-th Symbol in Grammer.md new file mode 100644 index 0000000..8a6d609 --- /dev/null +++ b/779. K-th Symbol in Grammer.md @@ -0,0 +1,99 @@ +### Step1 + +- まずは試しに全探索。これはまあダメ、TLEですね。予想通り。 + - よく考えたら文字列で集計せずに、最初からintにしてもよかった + +```python +class Solution: + def kthGrammar(self, n: int, k: int) -> int: + result = '0' + replaced_chars = [] + for _ in range(n - 1): + for c in result: + if c == '0': + replaced_chars.append('01') + else: + replaced_chars.append('10') + result = "".join(replaced_chars) + replaced_chars = [] + return int(result[k - 1]) +``` + +- 2進法ぽい + - 14番目は前の7番目から発生したもので、その中の2個目なので7番目の反転で〜と考えると + - 0-indexにすれば13番目→1101、は前の6番目→110(1101の右シフト1回)から出るけどラスト1なのでそれを反転する、みたいに考えれば、2進法のbit分だけ反転する。0番目の時は0なのでbitcountが奇数か判定すればOK + - 中学受験の算数ぽい(好きではある) + + ```python + + class Solution: + def kthGrammar(self, n: int, k: int) -> int: + return (k - 1).bit_count() % 2 + ``` + + +## Step2 + +- 再帰という手もあったか、思いつかなかった(というか発想がいきなり2進法にいった) + - k_prevとか設けずいきなり引数に突っ込んでもよかった + +```python + +class Solution: + def kthGrammar(self, n: int, k: int) -> int: + if n == 1: + return 0 + is_odd_index = k % 2 + k_prev = (k + 1) // 2 + if is_odd_index: + return self.kthGrammar(n - 1, k_prev) + else: + return 1 - self.kthGrammar(n - 1, k_prev) +``` + +- bitcountについての資料を読む + +https://stackoverflow.com/questions/109023/count-the-number-of-set-bits-in-a-32-bit-integer + +- x86というプロセッサなら命令がそのままあるらしい +- それ使わないやつが非常に面白い。(偶数bit + 奇数bit)によって先頭から2桁ずつbitcountして結果を2桁ずつに収める、先頭から4桁ずつbitcountして結果を4桁ずつに収める、を順に行う(説明が下手) +- [CPythonのコード](https://github.com/python/cpython/blob/main/Include/internal/pycore_bitutils.h#L95)の該当箇所も発見。#if (defined(**clang**) || defined(**GNUC**))であるならbuitinのpopcountを使ってx86のプロセッサの命令を使用。そうでなければ、上記のアルゴリズムでやってる(最後だけ掛け算使ってる?実質同じ操作だけど) +- Cのlongに収まらない場合は[これ](https://github.com/python/cpython/blob/main/Objects/longobject.c#L6266C1-L6266C19)がCPython実装?多倍長はstructの中の配列にn進法の各桁(nはoverflowしない範囲)が入っているので、単純に各要素で上記の関数を実行して足している +- 他参考資料 + +https://github.com/hayashi-ay/leetcode/pull/46 + +- [0, 1][1, 0]への辞書として持つ選択肢もあり +- https://github.com/goto-untrapped/Arai60/pull/26/files +- https://github.com/Exzrgs/LeetCode/pull/12/files + - kのbitcountをあえてiterativeに実装(right to leftでやってみた) + +```python + +class Solution: + def kthGrammar(self, n: int, k: int) -> int: + k -= 1 + result_digit = 0 + while k: + if k & 1: + result_digit ^= 1 + k >>= 1 + return result_digit +``` + +- 前半一緒なのと、それに対する反転とみて再帰 + - これなんでそうなるか考えたけど意外とむずい + +## Step3 + +```python + +class Solution: + def kthGrammar(self, n: int, k: int) -> int: + if n == 1: + return 0 + if k % 2 == 0: + return 1 - self.kthGrammar(n - 1, (k + 1) // 2) + else: + return self.kthGrammar(n - 1, (k + 1) // 2) +```