[프로그래머스] 광고 삽입 ( 2021 Kakao Recruitment)
알고리즘/프로그래머스

[프로그래머스] 광고 삽입 ( 2021 Kakao Recruitment)

 

 

 

광고 삽입

 

 

https://programmers.co.kr/learn/courses/30/lessons/72414

 

코딩테스트 연습 - 광고 삽입

시간을 나타내는 HH, H1, H2의 범위는 00~99, 분을 나타내는 MM, M1, M2의 범위는 00~59, 초를 나타내는 SS, S1, S2의 범위는 00~59까지 사용됩니다. 잘못된 시각은 입력으로 주어지지 않습니다. (예: 04:60:24, 11

programmers.co.kr

 

 

 

 

 

풀이

function solution(play_time, adv_time, logs) {
  // 시간 변경 함수
  const addZero = (time) => parseInt(time).toString().padStart(2, "0");
  const convertToHHMMSS = (time) =>
    addZero(time / 3600) + ":" +
    addZero((time % 3600) / 60) + ":" +
    addZero((time % 3600) % 60);
  const convertToSeconds = ([hour, min, sec]) => hour * 3600 + min * 60 + +sec;
  const changeTime = (time) => convertToSeconds(time.split(":"));

  // HH:MM:SS 형식을 초단위로 변경
  const playTime = changeTime(play_time);
  const advTime = changeTime(adv_time);
  const userLogs = logs.map((log) => log.split("-").map(changeTime));

  // 시간별로 조회수를 기록한 배열 생성 후 0으로 초기화
  let views = Array(playTime).fill(0);

  // logs를 돌며 들어가고 나가는 부분에 표시
  userLogs.forEach(([start, end]) => {
    views[start] += 1;
    views[end] -= 1;
  });

  // 누적 조회수를 만들기 위한 반복문
  const getCumulativeView = (arr) => {
    for (let i = 1, len = arr.length; i < len; i++) {
      arr[i] += arr[i - 1];
    }
  };

  getCumulativeView(views); // 시작, 끝부분에만 표시를 했기 때문에, 중간의 빈 시간을 채워줌
  getCumulativeView(views); // 한번 더 반복하면서 조회수가 누적됨 

  // 각 시간 별 누적 조회수를 비교
  const result = views.reduce(
    (max, view, index) => {
      const totalView = views[index + advTime] - view;
      return totalView > max[0] ? [totalView, index + 1] : max;
    },
    [views[advTime - 1] - views[0], 0]
  )[1];

  return convertToHHMMSS(result);
}

동영상의 길이는 00:00:01 이상 99:59:59 이하이다. 가장 긴 시간인 99:59:59 를 초단위로 환산하면 359999 이므로 광고 길이의 배열을 생성해도 문제 없다.

 

 

 

 

먼저, 주어진 시간들을 초단위로 변경해주었다.

  // HH:MM:SS 형식을 초단위로 변경
  const playTime = changeTime(play_time);
  const advTime = changeTime(adv_time);
  const userLogs = logs.map((log) => log.split("-").map(changeTime));
// 시간별로 조회수를 기록한 배열 생성 후 0으로 초기화
  let views = Array(playTime).fill(0);

동영상의 길이 만큼의 배열을 생성해주고 0으로 초기화 해준다. 여기에 누적 조회수가 담길 것이다.

 

초단위로 누적 조회수를 구해놓는다면 보다 쉽게 어떤 구간이 누적 조회수를 제일 많이 기록하는지 알기 쉽다.

만약 views = [ 0, 0, 1, 2, 2, 3, 4, 6, 8, 8] 이라면 4에서 7까지의 누적 조회수를 구할 때 views[7] - views[4] 만 해주면 값을 쉽게 구할 수 있다.

 

 

 

// logs를 돌며 들어가고 나가는 부분에 표시
  userLogs.forEach(([start, end]) => {
    views[start] += 1;
    views[end] -= 1;
  });

우선은 logs를 돌면서 들어가는 부분에 +1 나가는 부분에 -1을 더해 기록을 표시해준다.

 

 

// 누적 조회수를 만들기 위한 반복문
  const getCumulativeView = (arr) => {
    for (let i = 1, len = arr.length; i < len; i++) {
      arr[i] += arr[i - 1];
    }
  };

  getCumulativeView(views); // 시작, 끝부분에만 표시를 했기 때문에, 중간의 빈 시간을 채워줌
  getCumulativeView(views); // 한번 더 반복하면서 조회수가 누적됨

그리고 표시한 배열을 돌면서 이전 시간의 값을 더해준다.

한 번 반복하면 시작, 끝 부분에만 표시했던 조회수 기록을 채워주게 된다.

 

만약 10초의 동영상 길이에 logs가 2초 ~ 4초, 7초 ~ 9초, 5초 ~ 9초라면, 처음 시작, 끝부분만 표시한 배열은 [ 0, 0, 1, 0, -1, 1, 0, 1, 0, -2] 이 된다.

이 배열을 한번 순회하면서 이전 값을 더해준다면 [ 0, 0, 1, 1, 0, 1, 1, 2, 2, 0] 이되면서 중간에 비어있던 값들이 채워진다.

 

우리가 원하는건 시간 마다의 누적 시청자 수이기 때문에 한번 더 반복한다.

시간 별 시청자 수가 담긴 배열을 한번 더 순회하며 이전 값을 더해주면[ 0, 0, 1, 2, 2, 3, 4, 6, 8, 8이 된다. 이제 각 시간 까지의 누적된 시청자 수가 배열에 담기게 된다.

 

 

 

이제 다시 views를 돌면서 각 지점을 시작점으로 advTime 까지의 누적 시청자 수를 구한다.

// 각 시간 별 누적 조회수를 비교
  const result = views.reduce(
    (max, view, index) => {
      const totalView = views[index + advTime] - view;
      return totalView > max[0] ? [totalView, index + 1] : max;
    },
    [views[advTime - 1] - views[0], 0]
  )[1];

누적 시청자수 비교를 위해 누적 시청자 수와 결과적으로는 누적 시청자 수가 가장 많은 구간의 시작 지점을 반환해야하기 때문에 시작 시간을 기록해 놔야 한다. 따라서 .reduce의 리턴 값은 [누적 시청자수, 시작 시간 ( 배열의 인덱스 )] 로 해준다.

 

 

return convertToHHMMSS(result);

누적 시청자 수가 가장 많은 구간을 구했다면 시작 시간만을 가져와 다시 HH:MM:SS 형태로 변경 후 return해준다.