알고리즘

[프로그래머스] 봉인된 주문

moongi 2025. 5. 25. 22:44
728x90
반응형

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

 

프로그래머스

SW개발자를 위한 평가, 교육의 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

문제 유형

  • 구현

 

문제 난이도

  • Lev3

 

문제 분석

n 번째에 해당하는 문자열을 출력하는 문제이다. 그러나 조건이 있다.
1. 글자 수가 적은 주문부터 먼저 기록된다.
2. 글자 수가 같다면 사전 순서대로 기록된다.

ex)
"a"→"b"→"c"→"d"→"e"→"f"→...→"z"→"aa"→"ab"→...→"az"→"ba"→...→"by"→"bz"→"ca"→...→"zz"→"aaa"→"aab"→...→"aaz"→"aba"→...→"azz"→"baa"→...→"zzz"→"aaaa"→...→"aazz"→"abaa"→...→"czzz"→"daaa"→...→"zzzz"→"aaaaa"→...

3. 봉인된 주문서인 bans 의 문자열들은 제외하고 해당하는 문자열을 출력한다.


문제에서 n <= 10^15, bans[] 배열의 크기 <= 300,000 이므로 단순하게 완전 탐색을 한다면 시간 초과가 날 것이다.

 

풀이 방법

봉인된 주문서를 빠르게 탐색하기 위해 HashMap에 저장해서 사용하였고, count 배열을 통해, 금지된 주문서를 제외하고 현재 n 번째가 실제로 몇 번째 문자열인지 탐색하기 위해 사용하였다.

 

처음에 count[] 배열을 int 타입으로 선언해서 왜 안되지 혼자 계속 삽질했다. -> 26^11이면 당연히 long 타입이지 바보야

Map<String, Integer> map = new HashMap<>();
long[] count = new long[12];

count[1] = 26;

for(int i = 2; i < 12; i++) {
    count[i] = count[i-1] * 26;
}

for(int i = 0; i < bans.length; i++) {
    map.put(bans[i], 1);
    count[bans[i].length()]--;
}

 

 

size는 결과값의 글자 길이를 구하는 것이고, idx는 실제 금지된 주문서를 제외한 구해야하는 문자열이 몇 번째 수인지를 파악하는 변수이다.

answer 값에는 'z' 만큼 나눴을 떄, 나오는 문자열을 출력해준다.

long sum = 0;
        
int size = 0; // 글자 길이
long idx = 0; // 같은 자리수에서 구해야하는 순서
for(int i = 1; i < 12; i++) {

    if(sum + count[i] < n) sum += count[i];
    else {
        size = i;
        idx = n - sum - 1;
        break;
    }
}

int length = size;


for (int i = 0; i < size; i++) {
    long pow = (long) Math.pow(26, size - i - 1);
    answer += (char) (idx / pow + 'a');
    idx %= pow;
}

 

 

해당 부분은 앞서 구한 answer은 자신의 문자열 길이보다 작은 값에 대해서 idx를 구한 것이다.

예를 들면, 

 

n = 30, bans = ["d", "e", "bb", "aa", "ae"] 일때,

이전 상태에서 answer = "af"인 상태이다. (idx가 5이므로)

 

해당 상태는 문자열의 길이가 2일 때에 대한 봉인된 주문서를 제외한 상태가 아니므 현재 "af"와 문자열 길이가 같으면서 해당 문자열보다 내림차순에 있는 개수만큼 문자열의 순서를 늘려주어야 한다.

 long ct = 0;
for(String key : map.keySet()) {
    if(key.length() == length && answer.compareTo(key) >= 0) {
        ct++;
    }
}

 

 

해당하는 개수만큼에 대해 문자열을 오름차순한다. 증가시켰을 때, 'z' 다음 순서인 경우는 다음 자리로 올림해줘야하므로 다음과 같이 코드를 작성하였다.

 while(ct > 0) {

    char[] answers = answer.toCharArray();

    int upper = answer.length()-1;

    while(upper >= 0 && answers[upper] == 'z') {
        answers[upper] = 'a';
        upper--;
    }

    if(upper < 0) {
        answer = "a" + String.valueOf(answers);
        if(map.containsKey(answer)) ct++;
    }
    else {
        answers[upper] += 1;
        if(map.containsKey(String.valueOf(answers))) ct++;
        answer = String.valueOf(answers);
    }

    ct--;
}

전체 코드

import java.util.*;
import java.io.*;

class Solution {
    public String solution(long n, String[] bans) {
        String answer = "";
        
        Map<String, Integer> map = new HashMap<>();
        long[] count = new long[12];
        
        count[1] = 26;
        for(int i = 2; i < 12; i++) {
            count[i] = count[i-1] * 26;
        }
        
        for(int i = 0; i < bans.length; i++) {
            map.put(bans[i], 1);
            count[bans[i].length()]--;
        }
        
        long sum = 0;
        
        int size = 0; // 글자 길이
        long idx = 0; // 같은 자리수에서 구해야하는 순서
        for(int i = 1; i < 12; i++) {
            
            if(sum + count[i] < n) sum += count[i];
            else {
                size = i;
                idx = n - sum - 1;
                break;
            }
        }

        int length = size;
        
        
        for (int i = 0; i < size; i++) {
            long pow = (long) Math.pow(26, size - i - 1);
            answer += (char) (idx / pow + 'a');
            idx %= pow;
        }
        
        long ct = 0;
        for(String key : map.keySet()) {
            if(key.length() == length && answer.compareTo(key) >= 0) {
                ct++;
            }
        }
        
        while(ct > 0) {

            char[] answers = answer.toCharArray();
            
            int upper = answer.length()-1;
            
            while(upper >= 0 && answers[upper] == 'z') {
                answers[upper] = 'a';
                upper--;
            }
            
            if(upper < 0) {
                answer = "a" + String.valueOf(answers);
                if(map.containsKey(answer)) ct++;
            }
            else {
                answers[upper] += 1;
                if(map.containsKey(String.valueOf(answers))) ct++;
                answer = String.valueOf(answers);
            }
            
            ct--;
        }
           
        return answer;
    }
}
728x90
반응형