프로그래머스 가사검색 : programmers.co.kr/learn/courses/30/lessons/60060

 

코딩테스트 연습 - 가사 검색

 

programmers.co.kr

 

최근에 백준으로 이분탐색 관련 문제를 조금 풀었었는데 많은 도움이 되었습니다.

level4 분류에 있던데 '트라이' 자료구조를 사용했을 때 level4에 해당하는 문제같고

이분탐색으로 해결하면 level3 에 해당하는 문제가 아닌가 생각됩니다.

 

솔루션을 간단하게 정리하면 아래와 같습니다.

1. 이분탐색을 위해 words를 정렬해줍니다.

2. queries의 각 문자열 마다 words에 대한 '구간'을 구해줍니다.

3. '구간'탐색은 queries[i]의 각 문자와 인덱스를 가지고 words의 구간에서 lowerbound, upperbound를 찾습니다.

4. upper-lower는 현재 queries의 가능한 모든 경우의 수가 됩니다.

 

좀더 자세한 내용은 코드에 주석을 통하여 설명하였습니다.

코드가 140줄 가량으로 길긴하지만 

사실상 이분탐색을 위한 함수를 복붙한 것이라 solution 함수만 보시면 될 것 같습니다.

#include <algorithm>
#include <string>
#include <vector>

using namespace std;

vector<string> words;
vector<string> reverse_words;
vector<string> queries;

// 배열을 길이 우선, 문자열 순으로 정렬합니다.
bool cmp(string& a, string& b) {
    if (a.size() != b.size()) return a.size() < b.size();
    else if (a.size() == b.size()) return a < b;
}

// 길이에 대한 lower
int lower_start(int s, int e, int len) {
    int m;
    while (s < e) {
        m = (s + e) / 2;
        if (words[m].size() >= len) e = m;
        else s = m + 1;
    }
    return e;
}

// 길이에 대한 upper
int upper_start(int s, int e, int len) {
    int m;
    while (s < e) {
        m = (s + e) / 2;
        if (words[m].size() > len) e = m;
        else s = m + 1;
    }
    return e;
}

// words의 lower
int lowerbound(int s, int e, char ch, int idx) {
    int m;
    while (s < e) {
        m = (s + e) / 2;
        if (words[m][idx] >= ch) e = m;
        else s = m + 1;
    }
    return e;
}

// words의 upper
int upperbound(int s, int e, char ch, int idx) {
    int m;
    while (s < e) {
        m = (s + e) / 2;
        if (words[m][idx] > ch) e = m;
        else s = m + 1;
    }
    return e;
}

// reverse_words의 lower
int reverselowerbound(int s, int e, char ch, int idx) {
    int m;
    while (s < e) {
        m = (s + e) / 2;
        if (reverse_words[m][idx] >= ch) e = m;
        else s = m + 1;
    }
    return e;
}

// reverse_words의 upper
int reverseupperbound(int s, int e, char ch, int idx) {
    int m;
    while (s < e) {
        m = (s + e) / 2;
        if (reverse_words[m][idx] > ch) e = m;
        else s = m + 1;
    }
    return e;
}

vector<int> solution(vector<string> w, vector<string> q) {
    vector<int> answer;
    
    // 함수사용을 위해 전역으로 초기화하였습니다.
    words = w;
    queries = q;
    
    // 접미사부터 탐색해야하는 문자열을 위해
    // words 배열을 역으로 정렬하였습니다.
    reverse_words = words;
    for (int i = 0; i < reverse_words.size(); i++) 
        reverse(reverse_words[i].begin(), reverse_words[i].end());

    // cmp() 기준으로 정렬해줍니다.
    sort(words.begin(), words.end(), cmp);
    sort(reverse_words.begin(), reverse_words.end(), cmp);

    for (int i = 0; i < queries.size(); i++) {
        string str = queries[i];

        // 접두사인 경우
        if (str[0] != '?') {
            // 동일한 길이에 대한 비교를 위한 구간을 탐색합니다.
            int lower = lower_start(0, words.size(), str.size());
            int upper = upper_start(0, words.size(), str.size());

            // 접두사부터 words에 대입해보면서 이분탐색을 실행합니다.
            for (int i = 0; i < str.size(); i++) {
                if (str[i] == '?') break;

                // 현재 lower, upper는 탐색의 기준이 되기때문에 따로 사용합니다. 
                int l = lower;
                int u = upper;
                
                // 현재 문자str[i]의 lower, upper을 찾음
                lower = lowerbound(l, u, str[i], i);
                upper = upperbound(l, u, str[i], i);
            }
            // 구간끝-구간시작 이 가능한 모든 경우입니다.
            answer.push_back(upper - lower);
        }
        // 접미사인 경우(접두사와 같지만 words를 reverse_words로 사용!)
        else if (str[0] == '?') {
            int lower = lower_start(0, reverse_words.size(), str.size());
            int upper = upper_start(0, reverse_words.size(), str.size());
            reverse(str.begin(), str.end());
            for (int i = 0; i < str.size(); i++) {
                if (str[i] == '?') break;
                
                int l = lower;
                int u = upper;
                lower = reverselowerbound(l, u, str[i], i);
                upper = reverseupperbound(l, u, str[i], i);
            }

            answer.push_back(upper - lower);
        }
    }

    return answer;
}

 

 

** 효율성 실행시간

'algorithm > programmers' 카테고리의 다른 글

programmers 오픈채팅방  (0) 2020.12.10
Programmers 실패율  (0) 2020.12.10
Programmers 블록 이동하기  (0) 2020.12.08
Programmers 디스크 컨트롤러 C++  (0) 2020.08.20
Programmers 카펫 C++  (0) 2020.07.03

2020 KAKAO BLIND RECRUITMENT 문제 중 하나인

블록 이동하기 문제를 풀어봤습니다.

 

막힘없이 코드를 작성하긴 했지만,, 정말 오랜 시간이 걸렸기 때문에

이 문제를 풀면서 제 실력이 어느 수준인지 느낄 수 있었습니다.

 

움직일 수 있는 것은 문제에 주어졌듯이 2가지 형태입니다.

로봇 전체를 동서남북으로 움직이거나 // 둘 중 하나의 축을 기준으로 회전시키는 것입니다.

 

회전을 좀 더 간단하게 구현할 수 있었다면 좋았겠지만

딱히 방법이 떠오르지 않아 노가다로 조건을 주었습니다..

 

 

 

#include <string>
#include <vector>
#include <queue>
using namespace std;
struct robot{
    int y,x,side,time;
    /*
    side : 0, 1, 2, 3 // N, E, S, W
    */
};

int N;
int mx[4] = {0,0,-1,1};
int my[4] = {1,-1,0,0};
bool visit[100][100][4];
vector<vector<int> > board;

int getY(int y, int side){return side==0?y-1:(side==1?y:(side==2?y+1:y));}
int getX(int x, int side){return side==0?x:(side==1?x+1:(side==2?x:x-1));}

// ccw true : ccw || ccw false : cw
bool canMove(int y, int x, int side, int ccw){
    if(side==0){
        if(ccw){
            if(x-1>=0&&!board[y-1][x-1]&&!board[y][x-1]&&!visit[y][x][3]) return true;
        }
        else{
            if(x+1<N&&!board[y-1][x+1]&&!board[y][x+1]&&!visit[y][x][1]) return true;
        }
    }
    if(side==1){
        if(ccw){
            if(y-1>=0&&!board[y-1][x+1]&&!board[y-1][x]&&!visit[y][x][0]) return true;
        }
        else{
            if(y+1<N&&!board[y+1][x+1]&&!board[y+1][x]&&!visit[y][x][2]) return true;
        }
    }
    if(side==2){
        if(ccw){
            if(x+1<N&&!board[y+1][x+1]&&!board[y][x+1]&&!visit[y][x][1]) return true;
        }
        else{
            if(x-1>=0&&!board[y+1][x-1]&&!board[y][x-1]&&!visit[y][x][3]) return true;
        }
    }
    if(side==3){
        if(ccw){
            if(y+1<N&&!board[y+1][x-1]&&!board[y+1][x]&&!visit[y][x][2]) return true;   
        }
        else{
            if(y-1>=0&&!board[y-1][x-1]&&!board[y-1][x]&&!visit[y][x][0]) return true;
        }
    }
    return false;
}

int solution(vector<vector<int>> arr) {
    int answer = 0;
    board = arr;
    N = board.size();
    
    queue<robot> q;
    q.push({0,0,1,0});
    while(!q.empty()){
        int fy = q.front().y;
        int fx = q.front().x;
        int side = q.front().side;
        int sy = getY(fy, side);
        int sx = getX(fx, side);
        int time = q.front().time;
        q.pop();
        
        if((fy==N-1 && fx==N-1)||(sy==N-1&&sx==N-1)){
            answer = time;
            break;
        }
        
        if(visit[fy][fx][side]) continue;
        visit[fy][fx][side] = true;
        
        // move E,W,S,N
        for(int i = 0; i<4; i++){
            int ffy = fy+my[i];
            int ffx = fx+mx[i];
            int ssy = sy+my[i];
            int ssx = sx+mx[i];
            if(!(ffy>=0&&ffy<N&&ffx>=0&&ffx<N&&!board[ffy][ffx])) continue;
            if(!(ssy>=0&&ssy<N&&ssx>=0&&ssx<N&&!board[ssy][ssx])) continue;
            if(visit[ffy][ffx][side]) continue;
            q.push({ffy,ffx,side,time+1}); // use side var
        }
        
        // move one point
        // based fy, fx
        if(canMove(fy,fx,side,true)){
            for(int i = 0; i<4; i++){
                if(side == i) q.push({fy,fx,(i+3)%4,time+1});
            }
        }
        if(canMove(fy,fx,side,false)){
            for(int i = 0; i<4; i++){
                if(side==i) q.push({fy,fx,(i+1)%4,time+1});
            }
        }
        // based sy, sx
        if(canMove(sy,sx,(side+2)%4,true)){
            for(int i = 0; i<4; i++){
                if((side+2)%4 == i) q.push({sy,sx,(i+3)%4,time+1});
            }
        }
        if(canMove(sy,sx,(side+2)%4,false)){
            for(int i = 0; i<4; i++){
                if((side+2)%4 == i) q.push({sy,sx,(i+1)%4,time+1});
            }
        }
    }
    
    return answer;
}

'algorithm > programmers' 카테고리의 다른 글

Programmers 실패율  (0) 2020.12.10
Programmers 가사 검색  (0) 2020.12.09
Programmers 디스크 컨트롤러 C++  (0) 2020.08.20
Programmers 카펫 C++  (0) 2020.07.03
Programmers 숫자 야구 C++  (0) 2020.07.03

Programmers : https://programmers.co.kr/learn/courses/30/lessons/42627

 

코딩테스트 연습 - 디스크 컨트롤러

하드디스크는 한 번에 하나의 작업만 수행할 수 있습니다. 디스크 컨트롤러를 구현하는 방법은 여러 가지가 있습니다. 가장 일반적인 방법은 요청이 들어온 순서대로 처리하는 것입니다. 예를��

programmers.co.kr

 

level 3 , Heap 에 해당하는 문제입니다.

학부 수업시간 때, FIFO 부터 시작해서 OS 컨트롤러를 공부할 때 접해봤던 비슷한 문제입니다.

 

평균시간을 줄이기 위해 접근한 방법은

대기큐에 있는 작업 중에서! 가장 수행시간이 짧은 것 부터 처리하는 것! 으로 접근해봤습니다.

 

좀더 자세한 설명은 코드와 함께 주석으로 설명하였습니다.

 

#include <string>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;

typedef pair<int, int> pii;

int solution(vector<vector<int>> v) {
    int answer = 0;
    int time = 0;

    // 우선 큐에 대기 중인 프로세스 중에서 소요시간이 가장 짧은 것들을 우선적으로 처리하도록 접근
    // => 작업을 수행하고 있지 않을 때에는 먼저 요청이 들어온 작업부터 처리합니다.
    // 힙의 조건은 시간을 우선적으로 처리하되, 시간이 동일하면 사용시간이 짧은 녀석을 먼저 가져온다

    priority_queue<pii, vector<pii>, greater<pii> > q;

    // 내 나름 사용하기 편하게 변경
    vector<pii> jobs;
    for (int i = 0; i < v.size(); i++)
        jobs.push_back({ v[i][0], v[i][1] });
    
    // jobs의 값이 작업요청 순서대로 들어왔다는 말이 없으므로 정렬부터 함
    sort(jobs.begin(), jobs.end());

    // 먼저 들어온 작업부터 처리
    time = jobs[0].first + jobs[0].second;
    answer = jobs[0].second;
    
    int finish_task = 1;
    int task = 1;
    
    while(finish_task < jobs.size()){
        // 현재 time 까지 요청이 들어온 작업들을 큐에 넣고 작업
        for(int i = task; i < jobs.size(); i++){
            if(jobs[i].first <= time){
                // greater 로 꺼낼때 작업시간이 짧은 순으로 꺼내기 위해 swap 해서 푸쉬
                q.push({jobs[i].second, jobs[i].first});
                task++;
           }
            // time 보다 후에 들어온 작업은 우선처리 조건에 포함되지 않음
            else break;
        }
        
        // 현재 대기큐에 작업이 없다 -> 다음 작업을 가져옴 task        
        if(q.empty()){
            time = jobs[task].first+jobs[task].second;
            answer += jobs[task++].second;
            finish_task++;
        }
        // 현재 대기큐에 작업이 있다
        else{
            pii now_task = q.top();
            q.pop();
            finish_task++;
            answer += (time - now_task.second) + now_task.first;
            time += now_task.first;
        }
    }

    return answer/jobs.size();
}

'algorithm > programmers' 카테고리의 다른 글

Programmers 가사 검색  (0) 2020.12.09
Programmers 블록 이동하기  (0) 2020.12.08
Programmers 카펫 C++  (0) 2020.07.03
Programmers 숫자 야구 C++  (0) 2020.07.03
Programmers 소수 찾기 C++  (0) 2020.07.03

Programmers : https://programmers.co.kr/learn/courses/30/lessons/42842

 

코딩테스트 연습 - 카펫

Leo는 카펫을 사러 갔다가 아래 그림과 같이 중앙에는 노란색으로 칠해져 있고 테두리 1줄은 갈색으로 칠해져 있는 격자 모양 카펫을 봤습니다. Leo는 집으로 돌아와서 아까 본 카펫의 노란색과 ��

programmers.co.kr

Leo는 카펫을 사러 갔다가 아래 그림과 같이 중앙에는 노란색으로 칠해져 있고 테두리 1줄은 갈색으로 칠해져 있는 격자 모양 카펫을 봤습니다.

Leo는 집으로 돌아와서 아까 본 카펫의 노란색과 갈색으로 색칠된 격자의 개수는 기억했지만,

전체 카펫의 크기는 기억하지 못했습니다.

Leo가 본 카펫에서 갈색 격자의 수 brown, 노란색 격자의 수 yellow가 매개변수로 주어질 때

카펫의 가로, 세로 크기를 순서대로 배열에 담아 return 하도록 solution 함수를 작성해주세요.

 

programmers level2 카펫 문제입니다.

 

아주아주 단순한 문제입니다(?)

 

주어진 갈색 카펫의 수와, 노란 카펫의 수로 전체 카펫의 가로, 세로 크기를 알아내는 문제입니다

 

* 노란 카펫은 반드시 직사각형이 되어야 한다는 것. 이것만 아신다면 문제를 해결할 수 있을거라고 생각합니다.

 

주어진 노란카펫의 가로, 세로 높이를 임의의 수로 초기화 한 후에,

현재 가로, 세로 높이가 노란카펫을 직사각형으로 만들 수 있는지 없는지 확인해주시면서

갈색 카펫의 수를 예측해주시면 끝입니다.

 

저같은 경우 노란카펫의 높이를 1로 시작해서 찾아갔습니다.

 

간단한 규칙이 있다면 이정도겠네요

 

갈색카펫의 높이 중에서 위아래를 제외한 크기 = 세로카펫 높이*2

갈색카펫의 너비 = 세로카펫 너비 * 2 + 4

 

좀 더 자세한 설명은 주석을 적어놓았습니다~

 

#include <string>
#include <vector>

using namespace std;

vector<int> solution(int brown, int yellow) {
    vector<int> answer;
    
    // 카펫은 직사각형이라는 특징이 있으므로
    // 노란 카펫을 직사각형으로 만들어가면서, 갈색 카펫을 고려
    // => 노란 카펫의 높이가 1일때부터 고려해주면 된다
    
    int h = 1;
    int w = yellow;
    while(1){
        int bh, bw;
        // 갈색 카펫의 양쪽 높이의 위,아래 부분을 제외한 갯수는
        // 노란 카펫의 높이 * 2
        bh = h*2;
        
        // 갈색 카펫의 위아래 가로부분의 갯수는
        // 노란 카펫의 너비 * 2 + 4
        bw = w*2 + 4;
        
        if(bh + bw == brown){
            answer.push_back(bw/2);
            answer.push_back(bh/2+2);
            break;
        }
        else{
            // 노란 카펫의 갯수를 직사각형의 갯수에 맞게 조절해준다
            int check_h = h;
            int check_w = w;
            for(int i = 1; ; i++){
                if((check_h+i) * (yellow/(check_h+i)) == yellow){
                    h = check_h+i;
                    w = yellow/(check_h+i);
                    break;
                }
            }
        }
    }
    
    return answer;
}

'algorithm > programmers' 카테고리의 다른 글

Programmers 블록 이동하기  (0) 2020.12.08
Programmers 디스크 컨트롤러 C++  (0) 2020.08.20
Programmers 숫자 야구 C++  (0) 2020.07.03
Programmers 소수 찾기 C++  (0) 2020.07.03
Programmers 라면공장(C++)  (0) 2020.07.02

+ Recent posts