hyelie
hyelie
Hyeil Jeong
       
글쓰기    관리    수식입력
  • 전체보기 (495)
    • PS (283)
      • Algorithm (28)
      • PS Log (244)
      • Contest (6)
      • Tips (5)
    • Development (52)
      • Java (14)
      • Spring (23)
      • SQL (2)
      • Node.js (2)
      • Socket.io (3)
      • Study (4)
      • Utils (4)
    • DevOps (36)
      • Git (5)
      • Docker (4)
      • Kubernetes (2)
      • GCP (3)
      • Environment Set Up (8)
      • Tutorial (12)
      • Figma (2)
    • CS (74)
      • OOP (7)
      • OS (24)
      • DB (2)
      • Network (24)
      • Architecture (0)
      • Security (2)
      • Software Design (0)
      • Parallel Computing (15)
    • Project (15)
      • Project N2T (5)
      • Project ASG (0)
      • Project Meerkat (1)
      • Model Checking (7)
      • Ideas (2)
    • 내가 하고싶은 것! (34)
      • Plan (16)
      • Software Maestro (10)
      • 취준 (8)
hELLO · Designed By 정상우.
hyelie

hyelie

PS/PS Log

23.03.30. 풀었던 문제들

Leetcode 87. Scramble String

 어떤 string s1에 특정 연산을 반복해 s2를 만들 수 있는지 문제. DP 문제이다. 

 

 기본적인 직관은 s1의 substring이 s2의 scramble인지 확인하는 것이다. 또한, s1의 각 index에 대해 앞/뒤의 단어를 자르고, 또 앞뒤 단어를 바꿀 수 있기에 이를 모두 계산해 보면 된다.

 

 top-down으로 풀 때, s1의 index를 기준으로 s1, s2를 쪼개고 각각의 subproblem 4개를 풀 수 있다.

  • base case : s1 == s2이면 return true
  • s1의 앞쪽 substring과 s2의 앞쪽 substring && s1의 뒤쪽 substring과 s2의 뒤쪽 substring ... 1번, 2번
  • 순서를 바꾼 s1의 앞쪽 substring과 s2의 앞쪽 substring && 순서를 바꾼 s1의 뒤쪽 substring과 s2의 뒤쪽 substring ... 3번, 4번
  • (1번 && 2번) || (3번 && 4번) return true;

 이렇게 4개의 subproblem을 풀고, 1번 && 2번, 또는 3번 && 4번이 true이면 s1에 연산을 가해 s2를 만들 수 있다는 말이다. 예를 들어

s1 = great, s2 = rgeat

i = 1일 때
s1을 g / reat로 쪼개면 s1은 g / reat와 reat / g의 형태를 가질 수 있다. 따라서 
s1 : g / reat, s2 : r / geat
s1 : reat / g, s2 : rgea / t
이렇게 쪼개고 각각의 subproblem을 풀면 된다.

i = 2일 때
s1을 gr / eat로 쪼개면 s1은 gr / eat와 eat / gr의 형태를 가질 수 있다. 따라서 
s1 : gr / eat, s2 : rg / eat
s1 : eat / gr, s2 : rge / at
이렇게 쪼개고 각각의 subproblem을 풀면 된다.

 

 그러나 이렇게 풀면 T(n) = 4T(n-1) + O(n)이다. 약 O(n!)이다. n=30이므로 불가능. 그래서 memoization을 해야 한다. 이미 계산한 값이라면 계산하지 않도록. 일반적인 memoization이라면 dp[i][j]와 같은 배열을 사용하겠지? 그러나 이 문제의 경우 [s1의 시작점, s1의 끝점, s2의 시작점, s2의 끝점] 이렇게 4개의 index가 들어가므로 4차원 dp 배열을 만들어야 한다. 귀찮으므로 map을 하나 만들어서 퉁치면 쉽게 풀 수 있다.

 

// Runtime 177 ms Beats 39.14%
// Memory 37.1 MB Beats 32.82%

class Solution {
public:
    unordered_map<string, bool> um;

    bool isScramble(string s1, string s2) {
        if(s1 == s2) return true;

        string key = s1 + " " + s2;
        if(um.find(key) != um.end()) return um[key];

        for(int len = 1; len<s1.length(); len++){
            // x + y
            bool xy = isScramble(s1.substr(0, len), s2.substr(0, len)) && isScramble(s1.substr(len), s2.substr(len));
            
            if(xy){
                um[key] = true;
                return true;
            }   

            //isScramble(s1.substr(0, len), s2.substr(0, len)); // s1 앞부분, s2 앞부분
            //isScramble(s1.substr(len), s2.substr(len)); // s1 뒷부분, s2 뒷부분

            // y + x
           bool yx = isScramble(s1.substr(0, len), s2.substr(s2.length() - len)) && isScramble(s1.substr(len), s2.substr(0, s2.length() - len));
            if(yx){
                um[key] = true;
                return true;
            }
            //isScramble(s1.substr(0, len), s2.substr(s2.length() - len)); // s1의 앞부분, s2 뒷부분
            //isScramble(s1.substr(len), s2.substr(0, s2.length() - len)); // s1의 앞부분, s2 앞부분
        }
        um[key] = false;
        return false;
    }
};

 

시간복잡도

 [s1의 시작점, s1의 끝점, s2의 시작점, s2의 끝점]에 대해 각각 모든 연산을 하므로 O($n^{4}$)이다.

 

공간복잡도

 map에 들어가는 것은 해당 recurse의 개수와 동일할 것이다. 따라서 O(n^4).

 

후기

 상당히 어려웠다. 어떻게 똮! 하고 점화식을 내서 풀었는데 어떻게 풀었는제 모르겠다. 다음에 한 번 더 풀어봐야지.

 

 

 

 

저작자표시 (새창열림)

'PS > PS Log' 카테고리의 다른 글

23.04.01. 풀었던 문제들  (0) 2023.04.02
23.03.31. 풀었던 문제들  (0) 2023.03.31
23.03.29. 풀었던 문제들  (0) 2023.03.29
23.03.28. 풀었던 문제들  (0) 2023.03.28
23.03.27. 풀었던 문제들  (0) 2023.03.27
    hyelie
    hyelie

    티스토리툴바