Problem


문자열이 주어지면 알파벳과 특수문자를 조합해 최종적으로 화면에 입력된 문제를 출력하는 문제입니다.



chardescription
>move right
<move left
-back space


Solution

Stack 두 개를 이용하면 풀 수 있습니다.


스택 두개를 이용하여 현재 위치, 즉 커서를 표현합니다.


커서를 기준으로 두고 preFix 스택은 앞의 문자열, postFix 스택은 뒤의 문자열로 사용하면서 커서를 이동할때마다 스택끼리 값을 옮겨줍니다.


예제 1번을 따라가보겠습니다.






<<BP 까지 입력하면 preFix 스택에 아래 그림과 같이 값이 담길겁니다.


1



이 상태에서 한번 더 < 방향으로 이동을 한다면 P 를 오른쪽 스택으로 옮겨줍니다.

커서의 위치는 두 스택의 사이입니다.

2



위 상태에서 새로운 값 A를 추가한다면 왼쪽 스택에 들어갑니다.

문자열은 preFix + postFix 이기 때문에 현재 문자열은 BAP 입니다.

스택 두개를 기준으로 커서를 사용하기 때문에 문자를 추가하거나 뺄 때 O(1) 의 시간밖에 걸리지 않습니다.

3



위 상태에서 > 입력이 들어오면 P 를 왼쪽으로 이동시켜줍니다.

이동하는 이유와 현재 커서의 위치를 짐작할 수 있을겁니다.

왼쪽 스택이 비어있다면 커서의 위치는 맨 앞, 오른쪽 스택이 비어있다면 커서의 위치는 맨 뒤가 되는겁니다.


4


최종 문자열을 출력할 때는 preFix 에 있는 값을 postFix 로 전부 옮긴 후 출력하면 순서대로 나옵니다.

저는 번거로워서 그냥 Deque 썼습니다.



Java Code

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

class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();
        int n = Integer.parseInt(br.readLine());

        for (int i=0; i<n; i++) {
            String s = br.readLine();
            sb.append(getPassword(s));
            sb.append("\n");
        }

        System.out.println(sb);
    }

    static String getPassword(String input) {
        Deque<Character> preFix = new ArrayDeque<>();
        Deque<Character> postFix = new ArrayDeque<>();
        StringBuilder sb = new StringBuilder();

        for (int i=0; i<input.length(); i++) {
            char c = input.charAt(i);

            switch (c) {
                case '<':
                    if (!preFix.isEmpty()) {
                        postFix.addFirst(preFix.pollLast());
                    }
                    break;
                case '>':
                    if (!postFix.isEmpty()) {
                        preFix.addLast(postFix.pollFirst());
                    }
                    break;
                case '-':
                    if (!preFix.isEmpty()) {
                        preFix.pollLast();
                    }
                    break;
                default:
                    preFix.add(c);
            }
        }

        while (!preFix.isEmpty()) {
            sb.append(preFix.pollFirst());
        }

        while (!postFix.isEmpty()) {
            sb.append(postFix.pollFirst());
        }

        return sb.toString();
    }
}


Problem


N * M 크기의 미로가 주어졌을 때 (1, 1) 에서 (N, M) 위치로 가기 위한 최소 이동 칸 수를 구하는 문제입니다.



Solution

간단한 BFS 문제입니다.


Dot 클래스를 만들어서 (x, y) 좌표값과 현재까지 이동한 칸의 수 count 를 저장합니다.


BFS 를 돌면서 (N, M) 좌표에 도달했을 때 count 값을 출력하면 됩니다.



Java Code

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

class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] input = br.readLine().split(" ");
        int n = Integer.parseInt(input[0]);
        int m = Integer.parseInt(input[1]);
        int[][] maze = new int[n][m];
        
        for (int i=0; i<n; i++) {
            String s = br.readLine();

            for (int j=0; j<m; j++) {
                maze[i][j] = s.charAt(j) - '0';
            }
        }

        int result = bfs(n, m, maze);
        System.out.println(result);
    }

    static int bfs(int n, int m, int[][] maze) {
        int[] dx = {0, 0, -1, 1};
        int[] dy = {1, -1, 0, 0};

        Queue<Dot> q = new LinkedList<>();
        maze[0][0] = 0;
        q.add(new Dot(0, 0, 1));
        
        while (!q.isEmpty()) {
            Dot dot = q.poll();
            
            if (dot.x == n-1 && dot.y == m-1) {
                return dot.count;
            }

            for (int i=0; i<4; i++) {
                int nx = dot.x + dx[i];
                int ny = dot.y + dy[i];

                if (0 <= nx && nx < n && 0 <= ny && ny < m) {
                    if (maze[nx][ny] == 1) {
                        maze[nx][ny] = 0;
                        q.add(new Dot(nx, ny, dot.count + 1));
                    }
                }
            }
        }

        return 0;
    }

    static class Dot {
        int x, y, count;

        Dot(int x, int y, int count) {
            this.x = x;
            this.y = y;
            this.count = count;
        }
    }
}


문제 링크 : https://www.acmicpc.net/problem/16940


간단해보였는데 굉장히 많은 시도를 한 문제입니다.


처음에는 연결된 노드 갯수를 기준으로 하였고, 그 다음에는 depth를 기준으로 하였습니다.


그러나 이 문제의 핵심은 결국 어떤 정점은


1) 자신의 부모 정점이 무엇인가

2) 자신의 자식 정점은 몇개가 있는가


위 값들을 전부 아는 상태로 시작해야 합니다.


우선 처음엔 BFS 를 돌면서 부모 노드와 자식 노드의 갯수를 전부 기록해 둡니다.


그리고 주어진 순서를 쭉 순회하면서 현재 노드가 기록했던 부모 노드와 일치하는지 비교하면 됩니다.


parentQueue 라는 변수를 사용하였는데, 부모 노드만을 집어넣는 전용 큐입니다.


childSize 라는 자식 노드의 갯수가 1 이상인 노드들은 전부 parentQueue에 저장되며 자식 노드가 전부 나온 후에야 큐에서 빠져나옵니다.


만약 연속해서 나오는 자식 노드의 갯수가 저장된 childSize와 일치하지 않는다면 불가능한 BFS 라는 뜻이 되겠죠.



Problem


괄호만으로 이루어진 문자열이 주어졌을 때 VPS 인지 확인하는 문제입니다.


VPS 란 Valid PS 의 약자로 여는 괄호와 닫는 괄호가 한쌍을 이루는 문자열입니다.




Solution

원래는 스택을 활용하는 문제지만 사실 스택을 사용하지 않아도 됩니다.


문자열은 무조건 ( 또는 ) 만 들어오기 때문에


( 가 들어오면 count++ 


) 가 들어오면 count-- 


처리 해주면 쉽게 구할 수 있습니다.


만약 count 가 음수가 된다면 ( 없이 ) 가 들어왔다는 뜻입니다.


올바른 괄호 문자열이 될 수 없기 때문에 뒤의 문자들은 볼 필요 없이 VPS 가 될 수 없습니다.


문자열을 전부 돌고 난 다음에는 count 가 0 이 되어야 VPS 조건을 만족합니다.


count 가 1 이상이라면 ) 숫자보다 ( 숫자가 더 많다는 뜻이므로 VPS 가 될 수 없습니다.



Java Code

import java.io.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int n = Integer.parseInt(br.readLine());
        StringBuilder sb = new StringBuilder();
        
        for (int i=0; i<n; i++) {
            String result = isVPS(br.readLine()) ? "YES\n" : "NO\n";
            sb.append(result);
        }
        
        System.out.println(sb);
    }

    static boolean isVPS(String s) {
        int count = 0;

        for (char c : s.toCharArray()) {
            if (c == '(') {
                count++;
            } else {
                if (count == 0) return false;
                count--;
            }
        }

        return count == 0;
    }
}


문제 링크 : https://www.acmicpc.net/problem/10828


스택을 사용하는 문제입니다.


배열을 이용하면 속도는 훨씬 빠르지만 코틀린 스택 연습겸 사용하였습니다.


사실 자바랑 똑같아서 연습이랄 것도 없네요..


코드는 역시 코틀린이 훨씬 더 깔끔합니다.


문제 링크 : https://www.acmicpc.net/problem/1978


소수의 개수를 찾는 문제입니다.


숫자 number는 2 부터 number-1 까지 중에서 나눈 나머지가 0 이 되는 숫자가 존재한다면 소수가 아닙니다.


문제 링크 : https://www.acmicpc.net/problem/15904


간단한 문제입니다.


문장이 주어지면 대문자 U, C, P, C 가 순서대로 들어가 있는지 검사하면 됩니다.


순간적으로 contains 를 쓰면 쉽게 되지 않을까 했는데 


contains를 사용하면 CUPC 처럼 순서가 바뀌어도 "I love UCPC" 를 출력하기 때문에 한번 쭉 돌면서 일일히 비교하는 수밖에 없습니다.


Problem


LRU 캐시 교체 알고리즘(Least Recently Used)을 구현하면 됩니다.



Solution

LRU 알고리즘이란 가장 오랫동안 사용되지 않은 데이터를 교체하는 겁니다.


크기가 3 인 캐시에 [1, 2, 3, 1] 순서로 데이터가 들어갔다면 1 이 가장 오래된 데이터지만 가장 오랫동안 사용되지 않은 데이터는 2 입니다.


따라서 새로운 데이터 4 가 들어간다면 2 를 삭제하고 [3, 1, 4] 가 남게 됩니다.


LinkedHashSet 자료구조를 사용하여 구현하였습니다.


LinkedHashSet 은 HashSet 과 동일하게 삽입/검색/삭제 시간복잡도가 O(1) 이지만 내부적으로 연결리스트를 사용하여 순서가 보장됩니다.


캐시 안에 이미 도시 이름이 존재하면 기존에 있는 걸 삭제 후 다시 add 하여 갱신해줍니다.


캐시 안에 도시 이름이 존재하지 않으면 새로 추가합니다.


set.size() 가 cacheSize 를 넘어버린다면 가장 오랫동안 사용되지 않은 값을 삭제합니다.


삽입/삭제 구현 방식에 따라서 LinkedHashSet 의 첫번째 인덱스가 사용되지 않은 값이므로 set.iterator().next() 를 통하여 데이터를 가져와 삭제합니다.



Java Code

import java.util.*;

class Solution {
    public int solution(int cacheSize, String[] cities) {
        int answer = 0;
        
        if (cacheSize == 0) {
            return cities.length * 5;
        }
        
        Set<String> cache = new LinkedHashSet<>();
        
        for (String city : cities) {
            city = city.toLowerCase();
            
            if (cache.contains(city)) {
                cache.remove(city);
                answer += 1;
            } else {
                answer += 5;
            }
            
            cache.add(city);
            
            if (cache.size() > cacheSize) {
                String oldest = cache.iterator().next();
                cache.remove(oldest);
            }
        }

        return answer;
    }
}


+ Recent posts