출처: http://www.careercup.com/question?id=13817668
아마존 면접문제
A사무실에는 특정일자의 출퇴근 시간이 기록된 거대한 로그파일이 있다고 한다.
파일의 형식은 다음과 같다. (한 라인에서 앞부분은 출근시간(HH:MM:SS), 뒷부분은 퇴근시간이다)
09:12:23 11:14:35
10:34:01 13:23:40
10:34:31 11:20:10
특정시간을 입력(예:11:05:20)으로 주었을 때 그 시간에 총 몇 명이 사무실에 있었는지 알려주는 함수를 작성하시오.
이건 출근한 직원의 수, 그리고 저 '특정 시간'이 몇 개나 되느냐에 따라서 선택할 수 있는 전략이 크게 달라지는 문제인 것 같아요. 일단 질문의 수가 매우 많을 수 있다고 생각하고 구현했습니다
'특정 시간 t'에 사무실에 있는 사람들을 구하려면 (t보다 이전에 출근한 직원의 수) - (t보다 이전에 퇴근한 직원의 수) 가 됩니다.
질문의 수가 굉장히 많을 수 있다면 하나하나 카운팅하는 방법은 너무 오래 걸릴 수 있습니다. 그래서 직원들의 출근 시간을 정렬하고, 또 퇴근 시간을 정렬합니다. 그렇게 하고 나서 이진 탐색(binary search)를 통해 답을 찾았습니다.
class TimeSheet
def initialize
@enters =[]
@leaves = []
@sorted = true
end
def parse_time(timeStr)
h, m, s = timeStr.split(":").map(&:to_i)
h * 3600 + m * 60 + s
end
def register(enter_time, leave_time)
@enters << parse_time(enter_time)
@leaves << parse_time(leave_time)
@sorted = false
end
def upper_bound(array, value)
begin_index = 0
end_index = array.length
while begin_index < end_index
mid_index = (begin_index + end_index) >> 1
if array[mid_index] <= value
begin_index = mid_index + 1
else
end_index = mid_index
end
end
begin_index
end
def how_many?(query_time)
unless @sorted
@enters.sort!
@leaves.sort!
@sorted = true
end
if query_time.class == String
query_time = parse_time(query_time)
end
#for sorted array, it is better to use binary search
#if the number of query is small, you can use Array.count methods instead.
upper_bound(@enters, query_time) - upper_bound(@leaves, query_time)
end
end
ts = TimeSheet.new
File.open("attandance.log") do |f|
f.each_line do |line|
enter_time, leave_time = line.split(/\s+/)
ts.register(enter_time, leave_time)
end
end
querys = ["11:05:20", "10:35:00", "11:20:10"]
querys.each do |q|
puts ts.how_many?(q)
end
초단위라면, 어차피 24시간*60분*60초 = 24*3600 = 86400 이어서 배열로 만들면 됩니다.
배열을 2개 만듭니다.
1번 배열에는 +1, -1을 입력하고(출퇴근 기록) 2번 배열에는 누적된 합을 기록해 둡니다(해당 시간의 결과값)
로그를 읽을 때는 O(n), 값을 출력할 때는 2번 배열(결과값)에서 바로 출력하면 되므로 O(1)이 됩니다. 아래는 간단히 코드를 첨부합니다. (알고리즘 부분은 load()함수만 보시면 됩니다)
# -*- coding: utf-8 -*-
def time2sec(t):
h, m, s = map(int, t.split(":"))
return h*60*60+m*60+s
timesheet = [0]*24*60*60
manysheet = [0]*24*60*60
def load(inputs):
for input in inputs:
s, e = input.split(' ')
s = time2sec(s)
e = time2sec(e)
timesheet[s] += 1
timesheet[e] -= 1
manysheet[0] = timesheet[0]
for i in range(1, 24*60*60):
manysheet[i] = manysheet[i-1] + timesheet[i]
def howmany(time):
sec = time2sec(time)
print time, manysheet[sec]
input = [
'09:12:23 11:14:35',
'10:34:01 13:23:40',
'10:34:31 11:20:10'
]
if __name__ == "__main__":
load(input)
howmany('14:34:55')
2014/03/11 05:43
파이썬입니다.
# -*- coding: utf-8 -*-
import unittest
def sec(src):
h, m, s = map(int, src.split(":"))
return h*60*60+m*60+s
class Office:
def __init__(self):
self.emp = []
def add(self, e):
self.emp.append(e)
def count(self, t):
t = sec(t)
result = 0
for e in self.emp:
if e.s <= t <= e.e:
result += 1
return result
class Employee:
def __init__(self, s, e):
self.s = sec(s)
self.e = sec(e)
def compute(src, t):
office = Office()
src = src.strip()
for line in src.split("\n"):
s, e = line.split(" ")
office.add(Employee(s, e))
return office.count(t)
class Test(unittest.TestCase):
def test1(self):
office = Office()
e1 = Employee("09:12:23", "11:14:35")
office.add(e1)
self.assertEquals(1, office.count("11:05:20"))
def test2(self):
src = """
09:12:23 11:14:35
10:34:01 13:23:40
10:34:31 11:20:10
"""
self.assertEquals(3, compute(src, "11:05:20"))
if __name__ == "__main__":
unittest.main()
아마도 많은 분들이 이런방식으로 푸실것 같은데요..
아이디어는 다음과 같습니다.
파이썬은 다음과 같은 표현식이 가능합니다. 제가 파이썬을 좋아하는 이유중 하나죠 ^^
if 1 < a < 3:
2014/03/07 14:59
public class CountMember {
String[] times={"09:12:23 11:14:35","10:34:01 13:23:40","10:34:31 11:20:10"};
int cntMem=0;
public int count(String time){
cntMem=0;
for(String t : times){
int s=replaceTime(t.split(" ")[0]);
int e=replaceTime(t.split(" ")[1]);
int ti=replaceTime(time);
if(s<=ti && e>=ti){
cntMem++;
}
}
return cntMem;
}
public int replaceTime(String time){
return Integer.parseInt(time.replaceAll(":",""));
}
public static void main(String args[]){
System.out.println("count Member is ..."+new CountMember().count("11:05:20"));
}
}
java로 풀었습니다. 초단위로 변환하지 않고 : 만 빼서 숫자로 변환한다음에 비교했습니다. 어차피 초로환산해서 계산하는거나 마찬가지인거 같네요
2014/04/25 11:09
(def ^:dynamic *log-path* "./log")
(defn count-in-time [t]
(let [seconds (fn [[h m s]] (+ (* 3600 h) (* 60 m) s))
parse (fn [s] (->> (re-find #"(\d\d):(\d\d):(\d\d)" s)
rest
(map #(Integer/parseInt %))
seconds))
stayed? (fn [[i o]] (and (<= i (parse t))
(<= (parse t) o)))
lines (-> *log-path* clojure.java.io/file clojure.java.io/reader line-seq)
in-out (->> (map #(clojure.string/split % #" ") lines)
(map #(map parse %)))
stayed (filter stayed? in-out)]
(count stayed)))
Clojure 코드입니다.
테스트
=> (count-in-time "09:12:22")
0
=> (count-in-time "09:12:23")
1
=> (count-in-time "10:34:00")
1
=> (count-in-time "10:34:20")
2
=> (count-in-time "10:34:30")
2
=> (count-in-time "10:34:31")
3
=> (count-in-time "11:14:31")
3
=> (count-in-time "11:15:31")
2
=> (count-in-time "14:14:31")
0
Ruby
require 'time'
def count_people(times, time_criteria)
time_criteria = Time.parse(time_criteria)
times.split
.map{|time| Time.parse(time)}
.each_slice(2)
.map{|time_pair| time_pair.inject{|i, o| time_criteria > i && time_criteria < o}}
.count(true)
end
Test
require 'test/unit'
extend Test::Unit::Assertions
times = "09:12:23 11:14:35\n10:34:01 13:23:40\n10:34:31 11:20:10"
assert_equal count_people(times, "09:13:20"), 1
assert_equal count_people(times, "11:05:20"), 3
정말 많은 분들이 푸실만한 방법입니다. 간단히 sec 단위로 변경해서 그 범위내에서 있는지를 확인하는 식으로 처리했습니다. java 입니다.
public class OfficeCounter {
private class InOutLogger {
private final int inValue;
private final int outValue;
public InOutLogger(String logValue) {
String[] logNumberStrings = splitLogMessage(logValue);
if(logNumberStrings.length != 6) {
throw new IllegalArgumentException();
}
inValue = convertStringsToSec(logNumberStrings, 0);
outValue = convertStringsToSec(logNumberStrings, 3);
}
public boolean isExist(int sec) {
return sec >= inValue && sec < outValue;
}
}
public static String[] splitLogMessage(String logValue) {
return logValue.split("[:\\s]");
}
public static int convertStringsToSec(String[] numbers, int startIndex) {
return 60 * 60 * Integer.parseInt(numbers[startIndex]) +
60 * Integer.parseInt(numbers[1 + startIndex]) +
Integer.parseInt(numbers[2 + startIndex]);
}
private List<InOutLogger> inOutLoggers = new ArrayList<>();
public void add(String logValue) {
inOutLoggers.add(new InOutLogger(logValue));
}
public int count(String timeString) {
int sec = convertStringsToSec(splitLogMessage(timeString), 0);
int count = 0;
for(InOutLogger inOutLogger : inOutLoggers) {
if(inOutLogger.isExist(sec)) {
count++;
}
}
return count;
}
}
public class OfficeCounterTest {
private OfficeCounter officeCounter;
@Before
public void setUp() {
officeCounter = new OfficeCounter();
officeCounter.add("09:12:23 11:14:35");
officeCounter.add("10:34:01 13:23:40");
officeCounter.add("10:34:31 11:20:10");
}
@Test
public void countTest01() {
assertThat(officeCounter.count("11:05:20"), is(3));
}
}
2014/03/11 14:27
자바로 작성해봤습니다.
5년 만에 처음 코딩을 손에 잡은 DBA입니다. 많이 부족하지만 용기내서 올려봅니다.
import java.io.File;
import java.util.Scanner;
public class NumberOfManInOffice {
public static void main(String args[]) {
File file = null;
String line = null;
Scanner scanner = null;
String [] splitString = null;
String [] tempString = null;
int count=0;
int iArrival=0, iDeparture=0, iInput = 0;
String sArrival = "";
String sDeparture = "";
String sInput = "";
// 1. Read each line from test.txt
file = new File("C:\\test.txt");
// 2. Separate arrival and departure
try {
scanner = new Scanner(file);
while (scanner.hasNextLine()) {
sInput="";
sArrival="";
sDeparture="";
splitString = scanner.nextLine().split(" ");
tempString = args[0].split(":");
for(int i=0;i<tempString.length;i++) {
sInput = sInput + tempString[i];
}
tempString = splitString[0].split(":");
for(int i=0;i<tempString.length;i++) {
sArrival =sArrival+tempString[i];
}
tempString = splitString[1].split(":");
for(int i=0;i<tempString.length;i++) {
sDeparture = sDeparture+tempString[i];
}
iInput = Integer.parseInt(sInput);
iArrival= Integer.parseInt(sArrival);
iDeparture = Integer.parseInt(sDeparture);
System.out.println(iInput + " " + iArrival + " " + iDeparture);
if(iInput > iArrival && iInput <= iDeparture)
count ++;
}
System.out.println(count);
}
catch(Exception e) {
e.printStackTrace();
}
}
}
자바스크립트로 풀어봤습니다. 데이터는 body에 있다고 치고 작업했습니다 (body) 09:12:23 11:14:35 10:34:01 13:23:40 10:34:31 11:20:10 (/body) (script)
({
data: null,
input: null,
count: 0,
dataFollow: function () {
this.data = document.body.innerText;
},
init: function () {
return this.dataFollow(), this;
},
confirm: function () {
return this.input = prompt("시간을 입력해주세요", "HH:MM:SS"), this
},
formatData: function () { //data를 [[출근,퇴근],[출근,퇴근],..] , HHMMSS형식으로 변경
var tempParseArrData = this.data.replace(/[:]/g, "").match(/\d{6}\s\d{6}/g);
this.data = [],
tempArr = [],
this.input = this.input && parseInt(this.input.replace(/[:]/g, ""), 10);
for (var i = tempParseArrData.length; i--; ) tempArr = tempParseArrData[i].split(" "), this.data.push([parseInt(tempArr[0], 10), parseInt(tempArr[1], 10)]);
return this;
},
compare: function () { //data를 비교
var data = this.data, input = this.input;
for (var i = data.length; i--; ) (data[i][0] <= input && data[i][1] > input) ? this.count += 1 : "";
return this;
},
getData: function () {
data = this.data;
},
getResult: function () {
alert("현재 " + this.count + "명이 근무 중 입니다");
}
}).init().confirm().formatData().compare().getResult();
(/script)
2014/04/05 14:41
풀이 작성
코딩도장은 프로그래밍 문제풀이를 통해서 코딩 실력을 수련(Practice)하는 곳입니다.