네 번째 문제는 어려운 문제라서 순서대로 하지 않았습니다.코드는 테스트를 통과했지만 두 가지 수준의 고급 솔루션이 있으므로 신중하게 분류해야 합니다.5번 문제는 일시적으로 건너뜁니다.
LeetCode 브러싱 노트(Python3) - 6. Z자형 변환
(제목을 보려면 클릭)
(공식 솔루션을 보려면 클릭)
참고: 이 질문에 대한 공식 솔루션에는 Python 코드가 없지만 문제를 해결하는 두 가지 방법(행별 정렬 및 열별 정렬)을 제공합니다.
LeetCode 브러싱 노트(Python3) - 6. Z 모양 변환
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에 대해,
- 행 0의 문자는 인덱스 k( 2 ⋅ num R ows − 2 ) k(2⋅numRows−2) 에 있습니다.케이 ( 2⋅n u m 행 _ _ _-2 ) ;
- 가운데 줄 iii 의 문자는k( 2 ⋅ num Rows − 2 ) + ik(2⋅numRows−2)+i케이 ( 2⋅n u m 행 _ _ _-2 )+i以及( k + 1 ) ( 2 ⋅ num Rows − 2 ) − i (k+1)(2⋅numRows−2)−i( k+1 ) ( 2⋅n u m 행 _ _ _-2 )-내가 ;
- 行num Rows − 1 numRows−1n u m 행 _ _ _-1 의 문자는k ( 2 ⋅ num R ows − 2 ) + num R ows − 1 k(2⋅numRows−2)+numRows−1 에케이 ( 2⋅n 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입니다.) 이 원리는 실제로 "=="와 ">"와 "<"의 계산 원리와 관련이 있습니다. 후자의 두 가지가 확실히 더 빠릅니다(독자가 직관적으로 생각하도록 요청).