LeetCode Brushing Notes (Python3) - 6. Z자형 변환 (난이도: 중) (2021-05-12)

네 번째 문제는 어려운 문제라서 순서대로 하지 않았습니다.코드는 테스트를 통과했지만 두 가지 수준의 고급 솔루션이 있으므로 신중하게 분류해야 합니다.5번 문제는 일시적으로 건너뜁니다.

LeetCode 브러싱 노트(Python3) - 6. Z자형 변환
(제목을 보려면 클릭)
(공식 솔루션을 보려면 클릭)

참고: 이 질문에 대한 공식 솔루션에는 Python 코드가 없지만 문제를 해결하는 두 가지 방법(행별 정렬 및 열별 정렬)을 제공합니다.

1. 문제 해결 아이디어 및 알고리즘

이 문제를 해결하는 두 가지 방법이 있습니다. 열로 정렬하고 행으로 액세스하는 것입니다. ( 공식 솔루션 참조 )

1.1 열 기준 정렬

아이디어
우리는 문자열을 왼쪽에서 오른쪽으로 반복함으로써 문자가 지그재그의 어떤 줄에 있는지 쉽게 결정할 수 있습니다.

알고리즘 min(num Rows, len(s)) min(numRows,len(s))를
사용할 수 있습니다.min ( n u m Row s , _ _l e n ( s ))는 지그재그 패턴에서 비어 있지 않은 줄을 나타내는 목록입니다.

ss를 왼쪽에서 오른쪽으로 반복s , 각 문자를 해당 줄에 추가합니다. 현재 행 및 현재 방향 변수를 사용하여 해당 행을 추적할 수 있습니다.

위쪽 행으로 이동하거나 아래쪽 행으로 이동할 때만 현재 방향이 변경됩니다.

1.2 행별 액세스

아이디어
지그재그 패턴을 한 줄씩 읽는 것과 같은 순서로 문자열에 액세스합니다.

알고리즘은
먼저 행 0, 행 1, 행 2 등의 모든 문자를 방문합니다.

모든 정수 k에 대해,

  1. 행 0의 문자는 인덱스 k( 2 ⋅ num R ows − 2 ) k(2⋅numRows−2) 에 있습니다.케이 ( 2n u m _ _ _-2 ) ;
  2. 가운데 줄 iii 의 문자는k( 2 ⋅ num Rows − 2 ) + ik(2⋅numRows−2)+i케이 ( 2n u m _ _ _-2 )+i以及( k + 1 ) ( 2 ⋅ num Rows − 2 ) − i (k+1)(2⋅numRows−2)−i( k+1 ) ( 2n u m _ _ _-2 )-내가 ;
  3. num Rows − 1 numRows−1n u m _ _ _-1 의 문자는k ( 2 ⋅ num R ows − 2 ) + num R ows − 1 k(2⋅numRows−2)+numRows−1 에케이 ( 2n u m _ _ _-2 )+n u m _ _ _-1 곳;

2. 여러 솔루션 및 코드

초기 솔루션

# 时间复杂度:O(N) 空间复杂度:O(N)
class Solution:
    def convert(self, s: str, numRows: int) -> str:
        if numRows == 1:
            return s
        else:
            step = 2 * numRows - 2
            r = s[::step]
            for i in range(1, numRows - 1):
                a = s[i::step]
                b = s[step-i::step]
                # 将a和b交错合并(a和b的长度不一定相同)
                min_len = min(len(a), len(b))
                res = [''] * min_len * 2
                res[::2] = a[:min_len]
                res[1::2] = b[:min_len]
                r += ''.join(res) + a[min_len:] + b[min_len:]
            return r + s[numRows-1::step]

초기 솔루션은 행 단위 액세스의 아이디어를 채택하지만 (직관은 열별로 정렬하는 것이 매우 느릴 것이라고 생각하므로 일반 행 단위 액세스를 찾기 위해 직접 선택합니다) 실제로 두 개의 일반 하위 문자열이 있으므로 중간 행의 a와 b, for 루프를 사용하지 않고 파이썬에서 둘의 인터리빙 및 병합을 실현하기가 더 어려울 것이므로(구체적인 구현 방법은 여전히 ​​인터넷에서 찾을 수 있음) 실제 실행 속도는 매우 느린.

최적의 솔루션

공식 줄별 액세스 아이디어를 본 후 for 루프를 사용하여 중간 줄 문자를 읽도록 선택합니다.

class Solution:
    def convert(self, s: str, numRows: int) -> str:
        if numRows == 1:
            return s
        else:
            len_s = len(s)
            step = 2 * numRows - 2
            r = s[::step]
            for i in range(1, numRows - 1):
                for j in range(len_s):
                    pos_1 = step * j + i
                    if pos_1 < len_s:
                        r += s[pos_1]
                    else:
                        break

                    pos_2 = step * (j+1) - i
                    if pos_2 < len_s:
                        r += s[pos_2]
                    else:
                        break
            return r + s[numRows-1::step]

이 알고리즘은 여전히 ​​행 단위 액세스라는 아이디어를 사용하지만 원래 알고리즘보다 훨씬 빠르게 실행됩니다.

1. 슬라이스 사용 시 주의사항

a = "abcdefgh"
b = [::2]  # "aceg"

슬라이스의 두 개의 콜론으로 구분된 세 개는 start: stop: 단계, start는 시작 위치, 0이면 생략 가능, stop은 종료 위치 + 1(종료 위치가 아님에 주의), 바로 끝으로 가는 경우 생략 가능하며 step은 간격 크기입니다.

범위 함수의 세 매개변수도 위의 순서, 즉 범위( 시작, 중지[, 단계] )입니다.

가장 빠른 솔루션

실행 시간이 가장 짧은 코드를 참고하여 컬럼별로 정렬한 후 조인하는 아이디어입니다.

class Solution:
    def convert(self, s: str, numRows: int) -> str:
        if numRows < 2:
            return s
        n = len(s)
        lst = ['' for _ in range(numRows)]
        down = -1  # down = 1代表行数增加;反之,行数减少
        row = 0
        for ch in s:
            lst[row] += ch
            if row == numRows-1 or row == 0:
                down *= -1           
            row += down
        return ''.join(lst)

2. 여러 줄 문자열을 생성하는 방법

내가 개인적으로 작성할 수 없는 가장 중요한 이유 중 하나(초기 알고리즘을 작성할 때 하위 알고리즘을 쿼리해야 하는 필요성 포함)는 필요한 여러 줄 문자열을 생성하는 방법을 모른다는 것입니다. 실제로 생성 방법은 매우 간단합니다. 목록을 사용하기만 하면 됩니다.

lst = ['' for _ in range(numRows)]  # numRows为行数

또한 자리 표시자만 필요한 경우 변수를 정의하지 않고 "_"를 직접 사용할 수 있다는 것도 위의 코드에서 배울 수 있습니다.

3. join() 메소드 사용

( 루키 튜토리얼 참조 )
조인 방법은 시퀀스의 요소를 지정된 문자로 연결하여 새 문자열을 생성하는 데 사용됩니다. 구문은 다음과 같습니다.

str.join(sequence)

그 중 sequence는 연결할 요소의 순서(목록이나 사전도 가능하며, 사전을 사용할 때는 값 대신 키를 연결함), str은 문자를 순서대로 연결할 때 사용하는 중간 문자이다. 예를 들어,

'*'.join(['a','b','c'])  # 'a*b*c'
'-'.join(['a','b','c'])  # 'a-b-c'
''.join(['a','b','c'])  # 'abc'

4. 프로그램 속도를 높이는 팁: "같음" 및 "보다 큼/보다 작음"

숫자가 특정 값이고 다른 모든 가능한 값이 값보다 크거나 작은지 확인하려면 "<"(또는 ">")를 사용하는 것이 "=="를 사용하는 것보다 빠릅니다. 속도. 예를 들어, 가장 빠른 알고리즘의 형식 코드의 첫 번째 줄에

if numRows < 2:

~로 교체하다

if numRows == 1:

작업 속도가 크게 감소합니다. (이 문제의 두 연속 테스트에서 전자의 실행 시간은 52ms이고 후자는 60ms입니다.) 이 원리는 실제로 "=="와 ">"와 "<"의 계산 원리와 관련이 있습니다. 후자의 두 가지가 확실히 더 빠릅니다(독자가 직관적으로 생각하도록 요청).

Guess you like

Origin blog.csdn.net/AbaloneVH/article/details/116723740