Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions 6. Zigzag Conversion/6. Zigzag Conversion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# 6. Zigzag Conversion
## STEP1
- 何も見ずに解いてみる
- 言われたことをやる。numRows が 1 の時は先に処理しておくとよさそう。
- 最終行をよりよく書く方法があるかわからない。
```python
class Solution:
def convert(self, s: str, numRows: int) -> str:
if numRows == 1:
return s

row_to_letters = [[] for _ in range(numRows)]
direction = 1 # 1: going down, -1: going up
row_index = 0
for letter in s:
row_to_letters[row_index].append(letter)
if row_index == 0 and direction == -1:
direction = 1
elif row_index == numRows - 1 and direction == 1:
direction = -1
row_index += direction
return "".join("".join(row) for row in row_to_letters)
```
- 行の位置は計算できる。こちらの方がわかりやすいと思う人もいそう。
```python
class Solution:
def convert(self, s: str, numRows: int) -> str:
if numRows == 1:
return s

row_to_letters = [[] for _ in range(numRows)]
num_letters_in_cycle = 2 * numRows - 2
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この行は、2倍する意味や-2する意味のコメントがあると親切だと思いました。

for i, letter in enumerate(s):
position_in_cycle = i % num_letters_in_cycle
if position_in_cycle < numRows:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

row_index = min(position_in_cycle, num_letters_in_cycle - position_in_cycle) とも書けると思うのですが、パズルになってしまって読みにくくなるので、避けたほうが無難だと思います。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

私も同感です。

row_index = position_in_cycle
else:
row_index = num_letters_in_cycle - position_in_cycle
row_to_letters[row_index].append(letter)
return "".join("".join(row) for row in row_to_letters)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

join() が 2 重になっているのがやや読みづらく感じました。 2 行に分けると読みやすくなるかもしれません。

rows = ["".join(row) for row in row_to_letters]
return "".join(rows)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rows = ("".join(row) for row in row_to_letters)

とすると、Generator で副作用がある場合は等価ではないですね。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

差異があるケースは以下のようにあると思いますが、想定されているケースと合致していますか?

counter = 0


def f(row):
    global counter
    counter += 1
    return "".join(row) + str(counter)


row_to_letters = [["A"], ["B"], ["C"]]


counter = 0
rows = [f(row) for row in row_to_letters]
counter = 100
print("".join(rows))  # A1B2C3

counter = 0
rows = (f(row) for row in row_to_letters)
counter = 100
print("".join(rows))  # A101B102C103

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

そうですね。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。意外と気にすることが多いですね。
他には itertools.chain も使えるかなと思いました。
https://docs.python.org/3/library/itertools.html#itertools.chain

import itertools
print("".join(itertools.chain.from_iterable(row_to_letters)))

```

## STEP2
### プルリクやドキュメントを参照
- https://github.com/olsen-blue/Arai60/pull/61/files
- 進む方向をフラグで管理する書き方もある。たしかに、STEP1で書いた direction は方向だけと読まれる可能性あり。step はありかも。
- https://github.com/olsen-blue/Arai60/pull/61/files#r2040670667
- itertools.batched を使った面白解法。
- https://docs.python.org/3/library/itertools.html#itertools.batched
- https://github.com/olsen-blue/Arai60/pull/61/files#r2042314581
- 以下の二つは同じ
`"".join("".join(row_chars) for row_chars in rows)`
`"".join(c for row_chars in rows for c in row_chars)`
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

内包表現を 2 重にすると読みづらく感じます。原則避けたほうが良いと思います。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

あ、これ結果は同じですが、上は文字列を複数回 join していますが、下は Generator を利用して join 一回です。

- for の順番にあまり納得がいかない。
- https://docs.python.org/3/reference/expressions.html#generator-expressions をみると以下のよう。

generator_expression ::= "(" expression comp_for ")"
comp_for ::= ["async"] "for" target_list "in" or_test [comp_iter]
comp_iter ::= comp_for | comp_if

当てはめると
(1) c (2) for (3) row_chars (4) in (5) rows (6) for c in row_chars
(1) expression (2)"for" (3) target_list (4) "in" (5) or_test (6) comp_iter
のように読まれるので前の for が外側の for になる。全て追えていないがおそらく expression には comp_iter は含まれないため、逆の解釈はできない。

- https://github.com/hayashi-ay/leetcode/pull/71/files
- 4th の方向管理はシンプルでいいですね。
- row_index を先に進めてから、方向の反転をすればよい。

## STEP3
### 3回ミスなく書く
```python
class Solution:
def convert(self, s: str, numRows: int) -> str:
if numRows == 1:
return s

row_to_letters = [[] for _ in range(numRows)]
row_index = 0
direction = 1
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

私は bool の方が読みやすく感じますが好みの範囲だと思います。

for letter in s:
row_to_letters[row_index].append(letter)
row_index += direction
if row_index == 0 or row_index == numRows - 1:
direction *= -1
return "".join(c for row in row_to_letters for c in row)
```
練習として書いていて思ったが、Generator Comprehension の nest は書きにくいし読みにくい。

3分,2分,2分で3回Accept