프로그래머스/카카오

[프로그래머스/Level 3/C++/2021 카카오 블라인드 채용] 광고 삽입

Vfly 2023. 1. 11. 22:10

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

- 출처 : 프로그래머스 코딩 테스트 연습

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

[문제 요약]

- 다음은 "죠르디"가 공익광고가 삽입될 최적의 위치를 고르는 과정을 그림으로 설명한 것입니다.

  • 그림의 파란색 선은 광고를 검토 중인 "죠르디" 동영상의 전체 재생 구간을 나타냅니다.
    • 위 그림에서, "죠르디" 동영상의 총 재생시간은 02시간 03분 55초 입니다.
  • 그림의 검은색 선들은 각 시청자들이 "죠르디"의 동영상을 재생한 구간의 위치를 표시하고 있습니다.
    • 검은색 선의 가운데 숫자는 각 재생 기록을 구분하는 ID를 나타냅니다.
    • 검은색 선에 표기된 왼쪽 끝 숫자와 오른쪽 끝 숫자는 시청자들이 재생한 동영상 구간의 시작 시각과 종료 시각을 나타냅니다.
    • 위 그림에서, 3번 재생 기록은 00시 25분 50초 부터 00시 48분 29초 까지 총 00시간 22분 39초 동안 죠르디의 동영상을 재생했습니다. 
    • 위 그림에서, 1번 재생 기록은 01시 20분 15초 부터 01시 45분 14초 까지 총 00시간 24분 59초 동안 죠르디의 동영상을 재생했습니다.
  • 그림의 빨간색 선은 "죠르디"가 선택한 최적의 공익광고 위치를 나타냅니다.
    • 만약 공익광고의 재생시간이 00시간 14분 15초라면, 위의 그림처럼 01시 30분 59초 부터 01시 45분 14초 까지 공익광고를 삽입하는 것이 가장 좋습니다. 이 구간을 시청한 시청자들의 누적 재생시간이 가장 크기 때문입니다.
    • 01시 30분 59초 부터 01시 45분 14초 까지의 누적 재생시간은 다음과 같이 계산됩니다.
      • 01시 30분 59초 부터 01시 37분 44초 까지 : 4번, 1번 재생 기록이 두차례 있으므로 재생시간의 합은 00시간 06분 45초 X 2 = 00시간 13분 30초
      • 01시 37분 44초 부터 01시 45분 14초 까지 : 4번, 1번, 5번 재생 기록이 세차례 있으므로 재생시간의 합은 00시간 07분 30초 X 3 = 00시간 22분 30초
      • 따라서, 이 구간 시청자들의 누적 재생시간은 00시간 13분 30초 + 00시간 22분 30초 = 00시간 36분 00초입니다.

- "죠르디"의 동영상 재생시간 길이 play_time, 공익광고의 재생시간 길이 adv_time, 시청자들이 해당 동영상을 재생했던 구간 정보 logs가 매개변수로 주어진다.

- 시청자들의 누적 재생시간이 가장 많이 나오는 곳에 공익광고를 삽입하려고 합니다.

- 이때, 공익광고가 들어갈 시작 시각을 구해서 return 하도록 solution 함수를 완성해주세요. 만약, 시청자들의 누적 재생시간이 가장 많은 곳이 여러 곳이라면, 그 중에서 가장 빠른 시작 시각을 return 하도록 합니다.

 

[문제 조건]

  • play_time, adv_time은 길이 8로 고정된 문자열입니다.
    • play_time, adv_time은 HH:MM:SS 형식이며, 00:00:01 이상 99:59:59 이하입니다.
    • 즉, 동영상 재생시간과 공익광고 재생시간은 00시간 00분 01초 이상 99시간 59분 59초 이하입니다.
    • 공익광고 재생시간은 동영상 재생시간보다 짧거나 같게 주어집니다.
  • logs는 크기가 1 이상 300,000 이하인 문자열 배열입니다.
    • logs 배열의 각 원소는 시청자의 재생 구간을 나타냅니다.
    • logs 배열의 각 원소는 길이가 17로 고정된 문자열입니다.
    • logs 배열의 각 원소는 H1:M1:S1-H2:M2:S2 형식입니다.
      • H1:M1:S1은 동영상이 시작된 시각, H2:M2:S2는 동영상이 종료된 시각을 나타냅니다.
      • H1:M1:S1는 H2:M2:S2보다 1초 이상 이전 시각으로 주어집니다.
      • H1:M1:S1와 H2:M2:S2는 play_time 이내의 시각입니다.
  • 시간을 나타내는 HH, H1, H2의 범위는 00~99, 분을 나타내는 MM, M1, M2의 범위는 00~59, 초를 나타내는 SS, S1, S2의 범위는 00~59까지 사용됩니다. 잘못된 시각은 입력으로 주어지지 않습니다. (예: 04:60:24, 11:12:78, 123:12:45 등)
  • return 값의 형식
    • 공익광고를 삽입할 시각을 HH:MM:SS 형식의 8자리 문자열로 반환합니다.

 

[문제 풀이]

광고의 누적 재생시간이 가장 많이 나올려면 시청자가 많은 시간대에 광고를 배치하는 것이 가장 이상적일 것이다.

 

또한, 광고는 딱 한번만 영상에 들어가기 때문에 시청자가 가장 많은 시간대를 알아내야 할 필요가 있다.

 

매개변수로 주어지는 play_time의 최대가 99시간 59분 59초다. 이 값을 초로 변환하면 (360000-1)초가 되서 1차원 배열을 이용했고,  logs에 주어지는 시청자들의 시작시간과 종료시간을 앞에 배열에 표시하여 시청자가 가장 몰리는 시간대를 누적합을 이용해 찾았다.

 

처음에 logs의 원소 하나당 time_table[시작시간(초)] ~ time_table[종료시간(초)]의 값들을 하나씩 증가시킨 다음 누적합 형태로 만들어 접근했으나, 배열 자체가 워낙 크기 때문에 시간초과가 났었다.

 

그래서 전체 구간의 값들을 다 증가시키않고 시작과 끝만 증가시켜서 누적합을 2번 진행해주면 시간은 줄어들면서 같은 결과를 만들었다.

 

2번째 표가 의미하는 것은 그 구간에서 시청하는 시청자의 수를 의미한다.

 

하지만 우리가 원하는 것은 누적재생횟수를 구해야 하기 때문에 누적합을 한번 더 진행해준다.

이제 이 표를 이용하여 답을 구할 수 있다.

 

예를 들어 광고가 3초짜리 광고라 했을 때 확인 하는 구간들은

(1초~3초), (2초~4초), (3초~5초), (4초~6초), (5초~7초), (6초~8초), (7초~9초)

  • time_table[3초] - time_table[1초] = 3초
  • time_table[4초] - time_table[2초] = 5초
  • time_table[5초] - time_table[3초] = 6초
  • time_table[6초] - time_table[4초] = 5초
  • time_table[7초] - time_table[5초] = 4초
  • time_table[8초] - time_table[6초] = 3초
  • time_table[9초] - time_table[7초] = 1초

이렇게 구간 별 누적 재생시간을 구할 수 있고 3초일때 광고를 넣기 최고의 시간인 것이다.

 

[소스 코드]

#include <string>
#include <vector>
#include <iostream>

using namespace std;

vector<vector<long long>> make_time(string time)
{
    vector<vector<long long>> log_time;
    vector<long long> start_time;
    vector<long long> end_time;
    
    string tmp;
    bool flag=false;
    for(int i=0; i<time.size(); i++)
    {
        char letter = time.at(i);
        if(letter >= '0' && letter <= '9') tmp.push_back(letter);
        else if(letter == ':')
        {
            if(!flag)
            { start_time.push_back(atol(tmp.c_str())); tmp.clear(); }
            else
            { end_time.push_back(atol(tmp.c_str())); tmp.clear(); }
        }
        else if(letter == '-') 
        {
            start_time.push_back(atol(tmp.c_str())); tmp.clear();
            flag = true;
        }
    }
    if(tmp.size() != 0) end_time.push_back(atol(tmp.c_str()));
    
    log_time.push_back(start_time);
    log_time.push_back(end_time);
    
    return log_time;
}

long long hour_to_sec(vector<long long> &time)
{
    return (time[0] * 3600 + time[1] * 60 + time[2]);
}

string sec_to_hour(int time)
{
    string tmp;
    
    if(time / 3600 >= 10) { tmp=tmp+to_string(time/3600); tmp.push_back(':');time%=3600;}
    else {tmp.push_back('0'); tmp=tmp+to_string(time/3600); tmp.push_back(':'); time%=3600;}
   
    if(time / 60 >= 10) {tmp=tmp+to_string(time/60); tmp.push_back(':');time%=60;}
    else {tmp.push_back('0'); tmp=tmp+to_string(time/60); tmp.push_back(':');time%=60;}
    
    if(time >= 10) {tmp=tmp+to_string(time); }
    else {tmp.push_back('0'); tmp=tmp+to_string(time);}
    
    return tmp;
    
    
}

string solution(string play_time, string adv_time, vector<string> logs) {
    string answer = "";
    
    vector<long long> time_table; 
    vector<vector<vector<long long>>> log_time;
    
    for(int i=0;i<logs.size(); i++) log_time.push_back(make_time(logs[i]));
    
    string tmp = play_time + "-" + adv_time;
    vector<vector<long long>> tmp2 = make_time(tmp);
    vector<long long> play_time_int = tmp2[0];
    vector<long long> adv_time_int = tmp2[1];
    
    time_table.resize(hour_to_sec(play_time_int)+1);

    for(int i=0; i<log_time.size(); i++)
    {
        long long start_sec = hour_to_sec(log_time[i][0]);
        long long end_sec = hour_to_sec(log_time[i][1]);
        
        time_table[start_sec]++;
        time_table[end_sec]--;
    }
    
    for(int i=1; i<time_table.size(); i++)
        time_table[i] += time_table[i-1];
    
    for(int i=1; i<time_table.size(); i++)
        time_table[i] += time_table[i-1];
    
    long long pos = 0;
    long long ad_time = hour_to_sec(adv_time_int);
    long long max_v = time_table[ad_time - 1];
    for(int i=0; i+ad_time<=time_table.size(); i++)
    {
        if(max_v < time_table[i+ad_time] - time_table[i])
        {
            pos = i+1;
            max_v = time_table[i+ad_time] - time_table[i];
        }
    }
    answer = sec_to_hour(pos);
    return answer;
}

처음에 문제를 어떻게 접근해야될지 감도 안잡혀서 힌트를 얻고 풀긴했는데, 접근법을 떠올리는게 어려웠던 문제였던 거 같다.