Skip to content

Create Zigzag Conversion.md#6

Open
yakataN wants to merge 1 commit into
mainfrom
zigzag_conversion
Open

Create Zigzag Conversion.md#6
yakataN wants to merge 1 commit into
mainfrom
zigzag_conversion

Conversation

@yakataN
Copy link
Copy Markdown
Owner

@yakataN yakataN commented Jul 17, 2025

def zigzag_solution(self, s: str, numRows: int) -> str:
reordered_chars_list = [""] * numRows
height = 0
isForward = true
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

変数名を lower_snake に統一することをお勧めいたします。


#### step1
文字列とNが渡されて順番を規則的に動かすもの
方針1:個数NのVector(作成時点では配列長不明な配列)を作り、それぞれにPushしていく方式。Python3のstrの配列はこの条件を満たす。Stringのほうが簡便か。
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

str の += は毎回新しいインスタンスが作られるため、 list() に追加していく場合に比べて時間計算量が上がる点が気になりました。

https://github.com/python/cpython/blob/main/Objects/unicodeobject.c#L11603

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.

"str の += は毎回新しいインスタンスが作られる"ことを知らずに書いていました。手元で実験してみても数百倍の差があるので、ご指摘の通り、list()に追加する形のほうが良いですね。

import timeit

# 文字列に += で追加
code_str = "s = ''; [s := s + 'a' for _ in range(100000)]"

# リストに append
code_list = "ary = []; [ary.append('a') for _ in range(100000)]"

# 時間計測
number = 1

time_str = timeit.timeit(code_str, globals=globals(), number=number)
time_list = timeit.timeit(code_list, globals=globals(), number=number)

print(f"str += 'a': {time_str:.6f} 秒")
print(f"list.append('a'): {time_list:.6f} 秒")
~/Projects/sandbox $ python3 str_time.py
str += 'a': 0.976869 秒
list.append('a'): 0.002893 秒

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

計測方法に違和感を感じました。

  • += 演算子と := は別のものです。今回は += について論じているため、 += 演算子を使うべきです。
  • 今回の問題設定に合わせるのであれば、 ary に 'a' を append() したあと、 join() して一つの文字列にしなければならないと思います。

手元で計測しなおしたところ、異なる結果が出ました。

import timeit


def do_str():
    s = ""
    for _ in range(100000):
        s += "a"


def do_list():
    ary = []
    for _ in range(100000):
        ary.append("a")
        pass


def main():
    time_str = timeit.timeit(do_str, number=1000)
    print(f"str += \"a\": {time_str:.6f} 秒")

    time_list = timeit.timeit(do_list, number=1000)
    print(f"list.append(\"a\"): {time_list:.6f} 秒")


if __name__ == "__main__":
    main()
str += "a": 7.105431 秒
list.append("a"): 3.611834 秒

str += "a" のほう、 2 乗オーダーになると思っていたのですが、なっていないですね…。

調べ直したところ、 += 演算子は PyUnicode_Append() で処理されるようです。
https://github.com/python/cpython/blob/main/Objects/unicodeobject.c#L11650
また、 CPython では左辺値の参照カウントが 1 の場合、 in-place で追加されるため、高速になるようです。

ただ、この高速化手法はソフトウェアエンジニアの常識には含まれていない気がします。そのため、他のソフトウェアエンジニアが += のコードを読んだ時、 2 乗オーダーになるため遅くなるだろうと勘違いする可能性があります。ソースコードにコメントとして注意書きを書いておけば問題ないかもしれませんが、微妙なところです。
append() してから join() するほうは、平均的なソフトウェアエンジニアは線形だと分かると思いますし、ベンチマークの結果も速いため、こちらを使ったほうが良いと思います。 append() は動的配列で実装されており、 capacity が無くなるとメモリを再確保するものの、 1 回あたりの呼び出しの時間計算量は、償却時間 (ならし) で O(1) になる点に注意が必要です。 append() の挙動は常識に含まれると思いますので、一度調べておくとよいと思います。

for char in s:
reordered_chars_list[height] += char
if height + direction < 0 or height + direction >= numRows:
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.

*= -1 で符号反転は慣れると分かりやすいですね。

```python3
class Solution:
def convert(self, s: str, numRows: int) -> str:
if numRows == 0:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ここは numRows == 1 の間違いなのか、意図して0にしているのかどちらですか?
後者であるとすると、0の場合に元の文字列を返していることに違和感があります。また、height + direction が0と-1を行き来することになり、長さ1の reordered_chars_list に対してアクセスする要素が常に同じになるので問題なく動くのですが、読み解くのに時間がかかりました。
他のコードでは1になっていますね。

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.

すいません、前者です。
Pythonだとindexが-1でout of rangeにならず、動いてしまっているんですね

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants