Conversation
| n行k番目のシンボルはn-1行{(k+1)//2}番目をもとに決まり、kが奇数の時はn-1行目のシンボルと一緒、kが偶数の時はn-1行目のシンボルと反対するという性質をもとにn=nからn=1までloopで辿り、n=1の0と一緒か反対かを求めて返している。これでもいいと思うが、k番目の数というのをcolで表していて個人的にこれはn×mの二次元配列に使うべきで、今回のようにnの数によって長さが変わる文字列に対してインデックスをcolと名付けると誤解を生じやすいと感じた。kでいい気がする。 | ||
|
|
||
| https://github.com/hayashi-ay/leetcode/pull/46/changes | ||
| この人は最初に入力サイズを見て制約を考えていたので、自分も参考にして制約を考えてみる。1 <= n <= 30, 1 <= k <= 2^(n-1)ということで、愚直にnth rowまでの文字列を展開してk番目の文字を線形探索すると考えると、PythonのUTF-8文字列で '0' や '1' は 1 byteなので2^29byte≒1000^3/2=500MB。競プロでは250MBあたりがボーダーらしいので全ての文字を展開するのは現実的ではなさそう。線形探索も2^29≒10^9/2で、CPUが1秒に10^9回の単純演算を行うことができるのを考慮するとマックスで0.5秒で判断できる。これを考慮するとこの人がMemory Limit Exceedになったのが納得できる。 |
There was a problem hiding this comment.
PythonのUTF-8文字列で '0' や '1' は 1 byteなので
PythonのstrはUTF-8を使っていますか?
There was a problem hiding this comment.
https://peps.python.org/pep-0393/
ここを見るとASCII文字・BMP文字・絵文字など、文字列に含まれる最大の Unicode code point に応じてサイズが変わるみたいです。今回の場合は0, 1のみなので1文字につき1byteの固定長配列として保存されます。
ChatGPTには
「UTF-8というより、「ASCII範囲の Unicode code point を1 byte表現で持っている」と言うのが正確」と言われました。
| n行k番目のシンボルはn-1行{(k+1)//2}番目をもとに決まり、kが奇数の時はn-1行目のシンボルと一緒、kが偶数の時はn-1行目のシンボルと反対するという性質をもとにn=nからn=1までloopで辿り、n=1の0と一緒か反対かを求めて返している。これでもいいと思うが、k番目の数というのをcolで表していて個人的にこれはn×mの二次元配列に使うべきで、今回のようにnの数によって長さが変わる文字列に対してインデックスをcolと名付けると誤解を生じやすいと感じた。kでいい気がする。 | ||
|
|
||
| https://github.com/hayashi-ay/leetcode/pull/46/changes | ||
| この人は最初に入力サイズを見て制約を考えていたので、自分も参考にして制約を考えてみる。1 <= n <= 30, 1 <= k <= 2^(n-1)ということで、愚直にnth rowまでの文字列を展開してk番目の文字を線形探索すると考えると、PythonのUTF-8文字列で '0' や '1' は 1 byteなので2^29byte≒1000^3/2=500MB。競プロでは250MBあたりがボーダーらしいので全ての文字を展開するのは現実的ではなさそう。線形探索も2^29≒10^9/2で、CPUが1秒に10^9回の単純演算を行うことができるのを考慮するとマックスで0.5秒で判断できる。これを考慮するとこの人がMemory Limit Exceedになったのが納得できる。 |
There was a problem hiding this comment.
nth rowまでの文字列を展開してk番目の文字を線形探索
strのk番目の文字を確認するのに線形探索が必要でしょうか?
There was a problem hiding this comment.
普通に文字列[k-1]を見るだけでよかったですね。
There was a problem hiding this comment.
もしUTF-8のようにvariable-length encodingだったら、確かに線形探索が必要でしたね。
| n行k番目のシンボルはn-1行{(k+1)//2}番目をもとに決まり、kが奇数の時はn-1行目のシンボルと一緒、kが偶数の時はn-1行目のシンボルと反対するという性質をもとにn=nからn=1までloopで辿り、n=1の0と一緒か反対かを求めて返している。これでもいいと思うが、k番目の数というのをcolで表していて個人的にこれはn×mの二次元配列に使うべきで、今回のようにnの数によって長さが変わる文字列に対してインデックスをcolと名付けると誤解を生じやすいと感じた。kでいい気がする。 | ||
|
|
||
| https://github.com/hayashi-ay/leetcode/pull/46/changes | ||
| この人は最初に入力サイズを見て制約を考えていたので、自分も参考にして制約を考えてみる。1 <= n <= 30, 1 <= k <= 2^(n-1)ということで、愚直にnth rowまでの文字列を展開してk番目の文字を線形探索すると考えると、PythonのUTF-8文字列で '0' や '1' は 1 byteなので2^29byte≒1000^3/2=500MB。競プロでは250MBあたりがボーダーらしいので全ての文字を展開するのは現実的ではなさそう。線形探索も2^29≒10^9/2で、CPUが1秒に10^9回の単純演算を行うことができるのを考慮するとマックスで0.5秒で判断できる。これを考慮するとこの人がMemory Limit Exceedになったのが納得できる。 |
There was a problem hiding this comment.
CPUが1秒に10^9回の単純演算を行うことができるのを考慮するとマックスで0.5秒で判断できる
クロック数のことを指しているのなら、1GHzだと、かなり遅くないですかね?
また、クロック数を直接Pythonの実行時間の見積もりに使うのは、飛躍がありそうに思いました。
There was a problem hiding this comment.
今回目安に10^9を持ってきましたが秒数を導き出すのは難しそうですね。最初の方針を思いつく段階ではオーダー記法のみに言及するようにします。
There was a problem hiding this comment.
Pythonの場合は 10^7steps / secでざっくり考えています。総計算ステップ数が10^9/2であれば50 sくらいでしょうか
There was a problem hiding this comment.
50sもかかるんですね...目安を教えてくださりありがとうございます🙏
There was a problem hiding this comment.
こちらのコメントが参考になると思います。
Yuto729/leetcode#16
There was a problem hiding this comment.
ありがとうございます!10^6〜10^7/secで覚えておきます!
| return (k - 1)%2 | ||
| else: | ||
| #10 | ||
| return k%2 |
There was a problem hiding this comment.
(k - 1)%2と k%2だと、読み手が頭の中で計算しないと読めなさそうですかね?
There was a problem hiding this comment.
ネストを深くしたくなかったのでこう書いたのですが可読性的によくないですね。
converted = "01"
index = (k-1) % 2
return converted[index]
とかにしておけばよかったですね。
There was a problem hiding this comment.
それだと文字列を返していますね。index = (k-1) % 2も少し唐突かなと感じました。
return 1 if k % 2 == 1 else 0みたいにするのが、分かりやすいと思います。
| previous_val = self.kthGrammar(n - 1, (k + 1)//2) | ||
| if previous_val == 0: | ||
| #01 | ||
| return (k - 1)%2 |
There was a problem hiding this comment.
+, -は前後にスペースを入れて、/, *は全部詰めてたんですけどPEP8的には優先される演算子に関してはスペースなし、優先されない演算子にはスペースを開けるように書かれてました。
https://pep8-ja.readthedocs.io/ja/latest/#id15:~:text=%23%20%E6%AD%A3%E3%81%97%E3%81%84%3A%0Ai,%2D%20b)
# 正しい:
i = i + 1
submitted += 1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)
# 間違い:
i=i+1
submitted +=1
x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)
なのでこの場合は(k+1) // 2, return k % 2とかにした方が良さそうですね
There was a problem hiding this comment.
念のため、これは全部開けても全部空けなくてもいいが、優先度の高いところが空けたらそこよりも優先度の低いところは空けろということかと思います。
| ## step2: | ||
| ### code | ||
| ```python | ||
| class Solution: |
There was a problem hiding this comment.
class Solution:
def kthGrammar(self, n: int, k: int) -> int:
ks = []
for _ in range(1, n):
ks.append(k)
k = (k + 1) // 2
val = 0
for k in reversed(ks):
if val == 0:
val = (k - 1) % 2
else:
val = k % 2
return val
この解法だと空間計算量がO(N)かかりますけど1 <= n <= 30なので特に問題はないと思います。nが1000を超えそうなあたりから再帰でなくループを検討するのが良さそうですね。
There was a problem hiding this comment.
フリップした回数を数えると良いので、k = 1から始めなくてもできますね。
| 他の人のコードを見てみる。 | ||
| https://github.com/kitano-kazuki/leetcode/pull/47/changes | ||
| n行k番目のシンボルはn-1行{(k+1)//2}番目をもとに決まり、kが奇数の時はn-1行目のシンボルと一緒、kが偶数の時はn-1行目のシンボルと反対するという性質をもとにn=nからn=1までloopで辿り、n=1の0と一緒か反対かを求めて返している。これでもいいと思うが、k番目の数というのをcolで表していて個人的にこれはn×mの二次元配列に使うべきで、今回のようにnの数によって長さが変わる文字列に対してインデックスをcolと名付けると誤解を生じやすいと感じた。kでいい気がする。 |
There was a problem hiding this comment.
コードとは関係ない & もし意図してのことでしたら恐縮ですが,markdownでは2行分改行しないとレンダリング時に改行されません.
| if n == 1: | ||
| return 0 | ||
| previous_val = self.kthGrammar(n - 1, (k + 1)//2) | ||
| if previous_val == 0: |
There was a problem hiding this comment.
個人的には「偶奇によってprivious_valを反転するか決める」という意味を反映させた
if k % 2 == 0:
return 1 - previous_val
else:
return previous_valという書き方が好みです.
There was a problem hiding this comment.
これは流石に賛否両論ありそうですが,説明コメントを1行入れた上で
return (k + 1 - previous_val) % 2とかも1行でかけてシンプルかなと思います.
問題:https://leetcode.com/problems/k-th-symbol-in-grammar/description/
言語: Python3