문제 개요
각 로그에는 고유한 ID와 타임스탬프가 포함되어 있다고 가정해 보겠습니다. 타임스탬프는 년:월:일:시:분:초 형식의 문자열이며, 예를 들어 2019:01:01:23:59:59처럼 표현됩니다. 모든 필드는 0으로 채워진(zero-padded) 10진수입니다.
이제 다음 두 가지 기능을 제공하는 로그 저장 시스템을 설계해야 합니다.
- void Put(int id, string timestamp): 로그의 고유 ID와 타임스탬프를 전달받아 저장소에 보관합니다.
- int[] Retrieve(String start, String end, String granularity): start부터 end 사이 범위에 속하는 타임스탬프를 가진 로그들의 ID를 반환합니다. granularity 매개변수는 비교할 시간 단위를 지정합니다. 예를 들어 start = "2019:01:01:23:59:59", end = "2019:01:02:23:59:59", granularity = "Day"라면, 2019년 1월 1일부터 2019년 1월 2일 사이의 로그를 찾게 됩니다.
입력 예시
- put(1, "2019:01:01:23:59:59")
- put(2, "2019:01:01:22:59:59")
- put(3, "2018:01:01:00:00:00")
- retrieve("2018:01:01:01:01:01", "2019:01:01:23:00:00", "Year")
- retrieve("2018:01:01:01:01:01", "2019:01:01:23:00:00", "Hour")
첫 번째 retrieve 호출의 결과는 [1, 2, 3]입니다. 2018년~2019년 범위(연도 단위) 안에는 세 로그가 모두 포함되기 때문입니다. 반면 두 번째 호출의 결과는 [1, 2]입니다. 시간(Hour) 단위로 비교하면 2018:01:01:01부터 2019:01:01:23 사이의 로그만 해당하며, 로그 3은 이 범위에서 벗어나기 때문입니다.
해결 접근 방법
이 문제의 핵심은 granularity에 따라 타임스탬프 문자열을 어디까지 잘라서 비교할지 결정하는 것입니다. 해결 절차는 다음과 같습니다.
- 생성자(initializer)를 정의하고, 로그를 담을 빈 리스트
logs를 준비합니다. put()함수를 정의하여 전달받은 id와 timestamp를 logs 리스트의 끝에 추가합니다.retrieve()함수를 정의하여 시작 시간(s), 종료 시간(e), 시간 단위(gra)를 전달받습니다.- granularity에 따른 문자열 자르기 위치를 딕셔너리로 매핑합니다:
{'Year': 5, 'Month': 8, 'Day': 11, 'Hour': 14, 'Minute': 17, 'Second': 20} - start := s 문자열을 인덱스 0부터 index 위치까지만 자른 값
- end := e 문자열을 인덱스 0부터 index 위치까지만 자른 값
- logs의 각 (tid, timestamp)에 대해
start <= timestamp[:index] <= end조건을 만족하는 tid를 반환합니다.
타임스탬프 형식이 고정되어 있으므로 각 단위의 시작 인덱스는 일정합니다. 예를 들어 'Year'는 앞 4글자 뒤의 콜론까지 포함해 인덱스 5까지, 'Month'는 인덱스 8까지 자르면 됩니다. 이렇게 하면 문자열끼리 직접 비교하는 것만으로 원하는 시간 단위의 범위 검색이 가능합니다.
구현 예제
아래 코드를 통해 더 쉽게 이해할 수 있습니다.
class LogSystem(object):
def __init__(self):
self.logs = []
def put(self, id, timestamp):
self.logs.append((id, timestamp))
def retrieve(self, s, e, gra):
index = {'Year':5, 'Month' : 8, 'Day' : 11, 'Hour' : 14, 'Minute' : 17, 'Second' :20}[gra]
start = s[:index]
end = e[:index]
return (tid for tid, timestamp in self.logs if start <= timestamp[:index] <= end)
ob = LogSystem()
ob.put(1, "2019:01:01:23:59:59")
ob.put(2, "2019:01:01:22:59:59")
ob.put(3, "2018:01:01:00:00:00")
print(list(ob.retrieve("2018:01:01:01:01:01","2019:01:01:23:00:00","Year")))
print(list(ob.retrieve("2018:01:01:01:01:01","2019:01:01:23:00:00","Hour")))실행 결과
[1, 2, 3] [1, 2]
마무리
이 구현은 로그를 순차적으로 저장하고, 조회 시 granularity에 맞춰 타임스탬프를 부분 문자열로 잘라 비교하는 간단하지만 효율적인 방식입니다. 데이터 양이 매우 많아지면 이진 탐색이나 시간 단위별 인덱싱 등으로 성능을 further 최적화할 수 있지만, 문제의 요구사항을 충족하기에는 위 접근법이 가장 직관적이고 명확합니다.