문제

  • 문제 링크
  • 문제 설명: 문자열을 w개 단위로 잘라서 압축할 때 가장 짧은 길이를 반환 (문자열 길이는 1000 이하, 알파벳 소문자만 포함)
    • 예를 들어, "abcabcdede"와 같은 경우, 문자를 2개 단위로 잘라서 압축하면 "abcabc2de"가 되지만, 3개 단위로 자른다면 "2abcdede"가 되어 3개 단위가 가장 짧은 압축 방법이 된다.
  • 참고: 문자열은 제일 앞부터 정해진 길이만큼 잘라야 한다.
입력 출력
"aabbaccc" 7
"ababcdcdababcdcd" 9
"abcabcdede" 8
"abcabcabcabcdededededede" 14
"xababcdcdababcdcd" 17

풀이

  • 단순하게 생각하기: 문자 1개 단위로 압축했을 때 다음과 같은 코드를 실행하면 된다.
int compress(const string& s, int& n) {
    int len = 0;
    for (int i = 0, num_units = 1; i < n; i++) {
        if (s[i] == s[i+1]) num_units++; // 문자가 같으면 압축된 문자 단위 개수 증가
        else if (cnt > 1) { // 이전에 압축된 개수가 2개 이상이면
            len += to_string(num_units).size() + 1; // 압축된 길이를 계산 
            num_units = 1; // 문자 단위 개수 갱신
        } else len++; // 이전에 압축된 개수가 1인데 더 압축할 게 없으면 길이만 늘림
    }
    return len;
}
  • 더 나아가기: 문자 w개 단위로 압축할 경우 w개의 문자들이 연속되는지 검사하고 n 이 w로 나누어 떨어지지 않는 부분을 예외처리하면서 연속되지 않으면 w만큼 길이를 늘려주면 위와 동일하게 동작하는 코드를 작성할 수 있다.
int compress(const string& s, int& n, int& w) {
    int len = 0;
    for (int i = 0, j = w, num_units = 1; i < n; i+=w, j+=w) {
        if (s.substr(i, w) == s.substr(j, w)) num_units++; // 부분 문자열이 연속되면 개수 증가
        else if (num_units > 1) { // 개수가 1보다 크면 압축된 길이 추가
            len += to_string(num_units).size() + w;
            num_units = 1;
        } else len += i + w > n ? n % w : w; // 연속 개수가 1인데 압축할 게 없으면 남은 길이 추가
    }
    return len;
}
  • 길이가 1000 이하이기 때문에 compress의 시간복잡도가 O(N) 인 걸 감안하면, 브루트-포싱으로 매 w개 만큼 압축을 시도해서 최소 길이를 탐색해도 시간 초과가 발생하지 않는다. 참고로 w의 최대 길이는 주어진 문자열의 길이의 절반이다.
  • 정답 코드
#include <string>
#include <vector>
using namespace std;

int compress(const string& s, int& n, int& w) {
    int len = 0;
    for (int i = 0, j = w, num_units = 1; i < n; i+=w, j+=w) {
        if (s.substr(i, w) == s.substr(j, w)) num_units++;
        else if (num_units > 1) {
            len += to_string(num_units).size() + w;
            num_units = 1;
        } else len += i + w > n ? n % w : w;
    }
    return len;
}

int solution(string s) {
    int n = s.size();
    s += string(n >> 1, '.'); // 맨 끝에서 연속되는지 검사할 때 OOB 방지를 위해 패딩을 추가
    int answer = n;
    for (int w = 1; w <= n >> 1; w++) {
        answer = min(answer, compress(s, n, w));
    }
    return answer;
}

 

+ Recent posts