Longest Common Prefix
단어 배열 strs가 주어집니다. 모든 단어가 시작하는 가장 긴 문자열을 반환하세요. 모든 단어가 같은 문자로 시작하지 않으면 빈 문자열 ""을 반환하세요. 단어는 자기 자신의 접두사로 간주되므로, 단어가 하나뿐이라면 그 단어가 답입니다.
함수
- strsstring-array
- 비교할 단어
- 반환값string
- 모든 단어가 공유하는 가장 긴 접두사 또는 빈 문자열
제약 조건
1 ≤ strs.length ≤ 2001 ≤ strs[i].length ≤ 200- 모든 단어는 소문자 영문자만 포함합니다.
예제
- 입력
- strs = ["interview", "internet", "interval", "internal"]
- 출력
- "inter"
- 설명
- 네 단어 모두
inter로 시작합니다. 다음 위치에서interview와interval에는v가 있고,internet과internal에는n이 있으므로 접두사는 그 지점에서 끝납니다.
- 입력
- strs = ["stack", "queue", "heap"]
- 출력
- ""
- 설명
- 단어는
s,q,h로 시작합니다. 첫 글자부터 서로 다르므로 공통 접두사가 없고, 답은 빈 문자열입니다.
- 입력
- strs = ["prefix", "pre", "prepare"]
- 출력
- "pre"
- 설명
pre는 가장 짧은 단어이고 나머지 두 단어는 이 단어로 시작하므로, 이것이 정답입니다. 공통 접두사는 가장 짧은 단어보다 길 수 없습니다.
제출 시 숨은 테스트 +19개
후속 질문
목록은 고정되어 있고 여러 개의 질의 단어가 주어진다고 가정해 보세요. 매번 목록을 다시 훑지 않고도 각 질의에 대해 목록의 단어 중 적어도 하나와 공유하는 가장 긴 접두사를 어떻게 찾을 수 있을까요?
힌트
하나씩 열어 보세요. 열 때마다 조금 더 알려 줍니다.
정답은 가장 짧은 단어보다 길 수 없습니다. 정답에 포함되는 각 글자는 어떤 조건을 만족해야 할까요?
위치
i의 문자는 모든 단어에 위치i의 문자가 있고 그 문자들이 모두 같을 때만 답에 포함됩니다. 답은 이 조건이 처음으로 충족되지 않는 위치에서 끝납니다.첫 번째 단어의 위치를 왼쪽에서 오른쪽으로 확인하세요. 각 위치에서 다른 모든 단어를 확인하고, 그중 하나라도 너무 짧거나 글자가 다르면 해당 위치 앞까지의 첫 번째 단어 부분을 반환하세요.
풀이
모든 단어에서 같은 위치에 같은 문자가 있을 때만 그 문자를 답에 포함하며, 어떤 단어든 의견이 달라지거나 글자가 다 떨어지는 첫 위치에서 답이 끝납니다. 아래 두 접근 방식은 모두 단어를 문자 단위로 읽지만, 읽는 순서가 다릅니다. 열 단위로 훑으면 처음으로 의견이 다른 지점에서 멈추므로, 답에 해당하는 부분과 그다음 열까지만 읽습니다.
접두사를 단어별로 줄이기
핵심 아이디어
첫 번째 단어 전체가 답이라고 가정하고 시작합니다. 그런 다음 두 번째 단어와 글자 단위로 비교해 둘이 공유하는 부분만 남도록 줄입니다. 남은 부분을 세 번째 단어와 비교하고, 이런 식으로 계속합니다. 마지막 단어까지 비교하고 나면 모든 단어에 공통으로 남는 부분이 있습니다.
이 방법이 올바른 이유는 여러 단어의 공통 접두사가 첫 두 단어의 공통 접두사, 그 결과와 세 번째 단어의 공통 접두사, 이런 식으로 구할 수 있기 때문입니다. 각 단계에서는 접두사를 유지하거나 줄이기만 할 수 있습니다. interview, internet, interval, internal의 경우 후보는 두 번째 단어를 비교한 뒤 interview에서 inter로 줄어들고, 이후 그대로 유지됩니다.
각 글자는 최대 한 번 비교되므로 시간 복잡도는 O(S)입니다. 여기서 S는 전체 글자 수입니다. 복사본이 아니라 길이만 저장합니다. 약점은 순서입니다. 첫 199개 단어가 일치하고 마지막 단어의 첫 글자만 다른, 글자 수가 각각 200개인 단어 200개가 있다면, 마지막 단어가 접두사를 아무것도 남지 않게 줄이기 전에 첫 199개 단어 각각의 글자 200개를 모두 비교하게 되어 비교 횟수는 거의 40,000회에 달합니다.
알고리즘
prefixLen을strs[0]의 길이로 설정합니다.- 각각의 다른 단어에 대해
prefixLen을 넘지 않는 범위에서strs[0]과 앞부분의 글자가 몇 개 일치하는지 셉니다. prefixLen을 그 개수로 설정하고, 값이 0이 되면 일찍 중단합니다.strs[0]의 처음prefixLen개 글자를 반환합니다.
def longestCommonPrefix(strs):
first = strs[0]
prefix_len = len(first)
for word in strs[1:]:
common = 0
while common < prefix_len and common < len(word) and word[common] == first[common]:
common += 1
prefix_len = common
if prefix_len == 0:
break
return first[:prefix_len]열별로 비교
핵심 아이디어
단어를 표처럼 한 번에 한 열씩 살펴보세요. 열 0에는 모든 단어의 첫 글자가, 열 1에는 두 번째 글자가 들어가며, 이런 식으로 이어집니다. 현재 열에서 strs[0]의 글자를 가져와 다른 모든 단어의 같은 위치에 동일한 글자가 있는지 확인하세요. 어떤 단어의 글자가 처음으로 다르거나 해당 열에 글자가 없을 만큼 짧으면, 정답은 해당 열까지의 strs[0]입니다.
정답은 모든 단어가 일치하는 열이 연속해서 이어지는 부분이며, 이 루프는 그 열들을 왼쪽부터 살펴보다가 연속된 부분이 끊기는 첫 번째 열에서 멈춥니다. 끊기는 열이 없다면 strs[0] 자체가 정답입니다. 이 경우 strs[0]는 가장 짧은 단어이거나 가장 짧은 단어와 길이가 같습니다.
루프는 정답 다음 열까지 최대 한 열만 더 읽으므로, 단어가 n개이고 정답의 길이가 L일 때 최대 n × (L+1)번 확인합니다. 또한 단어의 같은 글자를 두 번 읽지 않으므로 O(S)이기도 합니다. 위의 경우처럼 199개 단어는 일치하고 마지막 단어는 첫 글자부터 다르면, 첫 번째 열을 확인한 뒤 멈춥니다. 거의 40,000번이 아니라 199번만 비교합니다.
알고리즘
first를strs[0]으로 설정합니다.first의 길이에서 1을 뺀 값까지, 0부터 각 열col에 대해first[col]을 읽습니다.- 다른 각 단어에
col위치의 문자가 없거나 해당 문자가 다르면,first의 처음col개 문자를 반환합니다. - 모든 열이 일치하면
first를 반환합니다.
def longestCommonPrefix(strs):
first = strs[0]
for col in range(len(first)):
for word in strs[1:]:
if col == len(word) or word[col] != first[col]:
return first[:col]
return first
함정과 경계 사례
답은 짧고 버그는 끝부분에 숨어 있습니다.
- 더 짧은 단어의 끝을 넘어 읽는 것.
prefix,pre,prepare에서 열 3은prefix에는 있지만pre에는 없습니다. 글자를 읽기 전에 길이를 확인하세요. - 주어진 순서에서 첫 번째 단어와 마지막 단어만 비교하는 것. 이 지름길을 쓰려면 먼저 단어를 정렬해야 합니다.
abc,xbd,abd에서 첫 번째 단어와 마지막 단어는ab를 공유하지만,xbd가 열 0에서 일치하지 않으므로 답은 빈 문자열입니다. - 공통 부분이 없을 때
null이나 자리 표시자를 반환하는 것. 답은 빈 문자열입니다. - 단어 하나가 자기 자신의 접두사라는 사실을 잊는 것:
algorithm하나만 있으면algorithm을 반환합니다. - 불변 문자열에 글자를 한 번에 하나씩 추가해 답을 만드는 것. 200글자 답이라면 복사본이 200개 생깁니다. 길이를 저장하고 마지막에 첫 번째 단어를 한 번만 잘라 사용하세요.
자주 묻는 질문4
Longest Common Prefix의 시간 복잡도는 얼마인가요?
두 스캔 모두 O(S) 시간이 걸리며, 여기서 S는 모든 단어에 있는 글자의 총개수입니다. 또한 답 외에는 O(1)의 추가 메모리만 필요합니다. 열 스캔의 시간은 n × (L+1)로도 제한되므로, 단어들이 시작 부분 근처에서 일치하지 않으면 일찍 종료됩니다.
단어를 정렬하여 가장 긴 공통 접두사를 찾을 수 있나요?
맞습니다. 알파벳순으로 정렬하면 첫 단어와 마지막 단어 사이에 있는 모든 단어는 두 단어가 공유하는 부분으로 시작하므로, 첫 단어와 마지막 단어만 비교하면 답을 구할 수 있습니다. 정렬에는 약 n log n개의 단어 쌍을 비교해야 하므로 한 번 훑는 것보다 비용이 더 들지만, 코드는 짧습니다.
공통 접두사가 없을 때 Longest Common Prefix는 무엇을 반환해야 하나요?
빈 문자열 ""을 반환합니다. stack, queue, heap처럼 두 단어가 서로 다른 문자로 시작하면 바로 그렇게 됩니다.
가로 스캔과 세로 스캔 중 어느 것이 더 나을까요?
둘 다 최악의 경우 O(S)입니다. 세로로, 열 단위로 스캔하는 것이 더 안전한 선택입니다. 어떤 단어든 일치하지 않는 첫 번째 열에서 멈추지만, 가로 스캔은 뒤늦게 다른 단어를 만나 비교가 중단되기 전에 긴 접두사를 여러 단어와 비교할 수 있습니다.
비슷한 문제
같은 아이디어를 쓰는 문제입니다. 두세 개를 풀면 패턴이 몸에 익습니다.
Python
def longestCommonPrefix(strs):
# 여기에 코드를 작성하세요케이스 1
케이스 2
케이스 3
입력
strs = ["interview", "internet", "interval", "internal"]
기대값
"inter"