Problem


매 초마다 움직이며 냄새를 뿌리는 상어들이 여러 마리 존재합니다.

각 상어들은 자신들이 바라보는 방향과 기준에 따라서 다음으로 움직일 방향을 선정합니다.

상어들이 같은 격자에 동시에 들어가게 되면 숫자가 작은 상어만 남고 나머지는 전부 내보냅니다.

가장 강한 1 번 상어가 남는 시간은 몇 초 뒤인지 구하는 문제입니다.



Solution

1 번 상어가 남거나 1000 초가 지날 때까지 다음 순서대로 반복하면 됩니다.

  1. moveShark() : 상어 이동
    • 다음 방향 계산
    • 이동
    • 경쟁을 통해 작은 번호의 상어 생존
  2. decreaseSmellTime() : 모든 냄새들 1 씩 감소
  3. createSmell() : 상어가 이동한 자리에 새로운 냄새 생성


주의할점

  1. 상어가 이동한 후에는 상어의 방향을 이동한 방향으로 바꿔주어야 합니다.
  2. 위 순서에서 2번과 3번을 바꾸게 되면 상어가 새로운 냄새를 생성하자마자 1 씩 깎이기 때문에 순서를 지켜야 합니다.
  3. 상어 리스트를 for 문으로 돌면서 경쟁에서 패배한 상어를 바로 제거하니 런타임 에러가 발생했습니다. for 문 도중에는 요소를 삭제하지 말고 기록해두었다가 나중에 삭제합니다.


Shark Class

  • 상어의 넘버, 좌표, 현재 방향과 우선 순위를 갖고 있습니다.
  • 우선 순위는 priority[현재 방향][우선 순위] 형태입니다.
  • findNextDir 함수는 상어가 다음으로 이동할 방향을 구합니다.
  • 현재 상어의 방향을 기준으로 우선순위가 높은 방향부터 candiates 셋에 존재하는지 확인합니다.
  • candidates 셋은 상어가 이동할 수 있는 방향들을 갖고 있습니다.


arr, smellOwner, leftTime

  • 각각 상어의 위치, 냄새의 주인, 냄새들의 남은 시간을 기록한 2 차원 배열입니다.


noSmellSet, mySmellSet

  • 상어가 이동할 수 있는 공간을 미리 구하기 위해 사용합니다.
  • 상어는 먼저 냄새가 없는 곳을 찾고 만약 4방향 전부 냄새가 존재한다면 자기 냄새가 있는 방향으로 이동합니다.
  • 이동할 수 있는 곳은 여러 개가 될 수 있기 때문에 Set 자료구조에 저장하고 상어의 현재 방향에 따른 우선순위를 통해 비교합니다.


willRemove

  • 경쟁에서 뒤쳐진 상어는 제거해야 합니다.
  • 한번에 여러 마리가 겹칠 수 있기 때문에 Queue 에 담아둡니다.



Java Code

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

class Main {
    // 1, 2, 3, 4 는 각각 위, 아래, 왼쪽, 오른쪽
    static class Shark {
        int id, x, y, dir;
        int[][] priority = new int[5][5];

        Shark() { }

        int findNextDir(Set<Integer> candidates) {
            for (int i = 1; i < 5; i++) {
                if (candidates.contains(priority[dir][i])) {
                    return priority[dir][i];
                }
            }
            return 0;
        }
    }

    static int N, M, k;
    static int[][] arr = new int[21][21];
    static int[][] smellOwner = new int[21][21];
    static int[][] leftTime = new int[21][21];
    static Map<Integer, Shark> sharks = new HashMap<>();
    static int time = 0;

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

        // input
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        k = Integer.parseInt(st.nextToken());

        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine());

            for (int j = 0; j < N; j++) {
                arr[i][j] = Integer.parseInt(st.nextToken());

                if (arr[i][j] > 0) {
                    Shark s = new Shark();
                    s.id = arr[i][j];
                    s.x = i;
                    s.y = j;
                    sharks.put(s.id, s);
                    
                    smellOwner[i][j] = s.id;
                    leftTime[i][j] = k;
                }
            }
        }

        // direction of sharks
        st = new StringTokenizer(br.readLine());
        for (int i = 0; i < M; i++) {
            Shark s = sharks.get(i + 1);
            s.dir = Integer.parseInt(st.nextToken());
        }

        // priority of sharks
        for (int i = 0; i < M; i++) {
            Shark s = sharks.get(i + 1);

            for (int j = 0; j < 4; j++) {
                st = new StringTokenizer(br.readLine());

                for (int z = 0; z < 4; z++) {
                    s.priority[j + 1][z + 1] = Integer.parseInt(st.nextToken());
                }
            }
        }

        // solution
        solution();
    }

    static void solution() {
        while (time++ < 1000) {
            moveShark();
            decreaseSmellTime();
            createSmell();

            if (sharks.size() == 1) {
                System.out.println(time);
                return;
            }
        }

        System.out.println(-1);
    }

    // 상어 이동 후 겹친 애 쫓아내기
    static void moveShark() {
        // dx dy 는 위, 아래, 왼쪽, 오른쪽 순서
        int[] dx = {-1, 1, 0, 0};
        int[] dy = {0, 0, -1, 1};
        Queue<Integer> willRemove = new LinkedList<Integer>();

        for (Integer id : sharks.keySet()) {
            Set<Integer> noSmellSet = new HashSet<>();
            Set<Integer> mySmellSet = new HashSet<>();
            Shark s = sharks.get(id);

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

                if (nx < 0 || nx >= N || ny < 0 || ny >= N) continue;

                if (smellOwner[nx][ny] == 0) {
                    noSmellSet.add(i + 1);
                } else if (smellOwner[nx][ny] == s.id) {
                    mySmellSet.add(i + 1);
                }
            }

            // 냄새 없는 곳부터 스캔하고 자기 냄새 있는 곳을 스캔해서 이동할 방향 구하기
            int nextDir = s.findNextDir(noSmellSet);

            if (nextDir == 0) {
                nextDir = s.findNextDir(mySmellSet);
            }

            // 상어 이동
            arr[s.x][s.y] = 0;
            if (nextDir == 1) {
                s.x -= 1;
            } else if (nextDir == 2) {
                s.x += 1;
            } else if (nextDir == 3) {
                s.y -= 1;
            } else if (nextDir == 4) {
                s.y += 1;
            }
            
            // 이동할 위치에 상어 있으면 경쟁 후 작은 번호가 승리
            if (arr[s.x][s.y] == 0 || s.id < arr[s.x][s.y]) {
                arr[s.x][s.y] = s.id;
                s.dir = nextDir;
            } else {
                willRemove.add(s.id);
            }
        }

        while (!willRemove.isEmpty()) {
            sharks.remove(willRemove.poll());
        }
    }

    // 맵 전체의 냄새 시간을 하나씩 감소 시키고 0 이 되면 냄새 주인 정보도 지움
    static void decreaseSmellTime() {
        for(int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (leftTime[i][j] == 0) continue;

                leftTime[i][j]--;

                if (leftTime[i][j] == 0) {
                    smellOwner[i][j] = 0;
                }
            }
        }
    }

    // 상어가 이동한 곳에 새로운 냄새 생성
    static void createSmell() {
        for (Integer id : sharks.keySet()) {
            Shark s = sharks.get(id);

            smellOwner[s.x][s.y] = s.id;
            leftTime[s.x][s.y] = k;
        }
    }
}


Problem


T 초가 지난 후 남아있는 미세 먼지들의 총 합을 구하는 문제입니다.



Solution

구현 문제입니다.

  1. 미세먼지 확산
  2. 공기청정기 가동


주의할 점은 미세먼지 확산 시 기존 배열 (아래 코드에서는 arr) 을 바로 수정하지 않는 겁니다.

예를 들어 (1, 1) = 5, (1, 2) = 9 의 미세머지가 있다고 가정합니다.

만약 기존 배열을 그대로 수정하면 (1, 1) 에 있던 미세먼지를 먼저 확산시켜서 (1, 2) 의 미세먼지가 10 이 되고

반복문이 (1, 2) 위치에 도달했을 때 10 을 기준으로 확산하기 때문에 주변에 2 씩 퍼지게 됩니다.

그래서 미세먼지의 확산 결과를 temp 배열에다가 담았다가 arr 배열에 덮어쓰는 형식으로 구현했습니다.

공기청정기는 그냥 노가다로 구현했습니다.



Java Code

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

class Main {
    static int R, C, T;
    static int[][] arr = new int[50][50];
    static List<Integer> airCleanerRows = new ArrayList<>();
    static int sumOfDust = 2;

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

        // input
        st = new StringTokenizer(br.readLine());
        R = Integer.parseInt(st.nextToken());
        C = Integer.parseInt(st.nextToken());
        T = Integer.parseInt(st.nextToken());

        for (int i = 0; i < R; i++) {
            st = new StringTokenizer(br.readLine());

            for (int j = 0; j < C; j++) {
                arr[i][j] = Integer.parseInt(st.nextToken());
                sumOfDust += arr[i][j];

                if (arr[i][j] == -1) {
                    airCleanerRows.add(i);
                }
            }
        }

        // solution
        solution();
    }

    static void solution() {
        while (T-- > 0) {
            // 1. 먼지 확산
            arr = spreadDust();

            // 2. 공기청정기 가동
            executeAirCleaner();
        }

        System.out.println(calculateSum());
    }

    static int[][] spreadDust() {
        int[][] temp = new int[50][50];
        int[] dx = {-1, 1, 0, 0};
        int[] dy = {0, 0, -1, 1};

        // 확산된 미세먼지를 temp 배열에 계산
        for (int x = 0; x < R; x++) {
            for (int y = 0 ; y < C; y++) {
                if (arr[x][y] == -1) {
                    temp[x][y] = -1;
                    continue;
                }

                temp[x][y] += arr[x][y];

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

                    if (nx < 0 || nx >= R || ny < 0 || ny >= C) continue;
                    if (arr[nx][ny] == -1) continue;

                    temp[nx][ny] += (arr[x][y] / 5);
                    temp[x][y] -= (arr[x][y] / 5);
                }
            }
        }

        return temp;
    }

    static void executeAirCleaner() {
        // 위쪽 공기청정기는 반시계방향
        int top = airCleanerRows.get(0);

        for (int x = top - 1; x > 0; x--) {
            arr[x][0] = arr[x-1][0];
        }
        
        for (int y = 0; y < C - 1; y++) {
            arr[0][y] = arr[0][y+1];
        }

        for (int x = 0; x < top; x++) {
            arr[x][C-1] = arr[x+1][C-1];
        }
        
        for (int y = C - 1; y > 1; y--) {
            arr[top][y] = arr[top][y-1];
        }

        arr[top][1] = 0;
        

        // 아래쪽 공기청정기는 시계 방향
        int bottom = airCleanerRows.get(1);
        
        for (int x = bottom + 1; x < R - 1; x++) {
            arr[x][0] = arr[x+1][0];
        }

        for (int y = 0; y < C - 1; y++) {
            arr[R-1][y] = arr[R-1][y+1];
        }

        for (int x = R - 1; x > bottom; x--) {
            arr[x][C-1] = arr[x-1][C-1];
        }

        for (int y = C - 1; y > 1; y--) {
            arr[bottom][y] = arr[bottom][y-1];
        }

        arr[bottom][1] = 0;
    }

    static int calculateSum() {
        int sum = 2;

        for (int x = 0; x < R; x++) {
            for (int y = 0; y < C; y++) {
                sum += arr[x][y];
            }
        }
        
        return sum;
    }
}


Problem


모든 승객들을 도착지에 옮겨주고 난 뒤 남은 연료의 양을 출력하는 문제입니다.

승객들을 데리러 가는 기준은 가까운 기준이며 같은 거리에 여러 명의 승객이 있다면 행 번호가 가장 작고 열 번호가 가장 작은 승객을 데리러 갑니다.



Solution

귀찮은 구현 문제입니다.

아래 과정을 계속 반복하면 됩니다.

  1. BFS 를 사용하여 승객 찾기
  2. 가장 가까운 승객 비교
  3. 도착지에 데려다줌
  4. 연료 확인


주의사항도 있습니다.

  1. A 의 도착지와 B 의 출발지가 같은 경우 존재 -> 택시의 현재 위치도 검사
  2. 도착지는 겹칠 수 있음 -> 도착지를 특정해놓고 BFS 를 진행
  3. 행과 열 우선순위 헷갈리지 않게 주의
  4. 연료는 많은데 벽에 막혀서 출발지 또는 도착지로 가지 못하는 경우 -> 승객을 태우거나 내리지 못했는데 BFS 가 끝나진 않았는지 확인


변수

변수설명
Taxi Class택시 클래스입니다. BFS 에서 좌표를 담아두기 위해 사용하며 택시가 이동한 거리인 move 변수를 갖고 있습니다.
Passenger Class승객 클래스입니다. 승객의 아이디, 출발지와 도착지 좌표를 갖고 있습니다. 아이디는 2 부터 시작합니다.
taken현재 타고 있는 승객입니다. null 이면 아무도 타지 않은 상태, 누군가 타고 있다면 해당 승객 객체가 들어갑니다.
passMap승객 리스트입니다. 승객이 도착지에 내릴 때마다 해당 승객을 지워가면서 모든 승객이 내렸는지 체크합니다.
candidates택시로부터 같은 거리에 있는 승객들입니다. 택시에 탈 수 있는 후보들이며 행과 열을 비교하기 위해 사용합니다.


가장 가까운 승객 비교하기

Taxi 클래스에 있는 move 변수를 이용하면 특정 좌표에 도달했을때 택시 위치로부터 얼만큼 이동했는지 알 수 있습니다.

BFS 를 진행하면서 승객 위치에 도달하면 candidates 큐에 담아둡니다.

prevMove 변수를 사용해서 이전 move 를 기억하고 있다가 둘이 달라지는 순간에 candidates 큐에 값이 있는지 없는지 검사합니다.

만약 값이 없으면 계속 BFS 를 진행하면 되고 값이 있다면 최단거리인 승객들을 이미 찾았기 때문에 break 로 빠져나옵니다.

candidates 큐에 있는 승객들은 모두 거리가 같기 때문에 행과 열을 비교하여 조건에 맞는 승객을 찾아서 태웁니다.

우선순위 큐를 쓰면 큐에 넣을때마다 매번 비교를 하기 때문에 그냥 일반큐로 하고 나중에 비교했습니다.


도착지에 데려다줌

taken 으로 타고 있는 승객의 정보를 알 기 때문에 도착지 좌표에 도달할 때까지 BFS 를 진행하면 됩니다.


연료 확인

BFS 가 끝나면 prevMove 값을 반환합니다.

이동한 거리, 즉 사용한 연료기 때문에 전체 연료량과 연산한 뒤 음수가 되는지 확인하면 됩니다.

벽에 막혀서 연료가 충분한데 도달할 수 없거나 이동 중에 연료가 떨어지면 Integer.MAX_VALUE 를 리턴하여 fuel 값을 음수로 만들어줍니다.



Java Code

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

class Main {
    static class Taxi {
        int x, y, move;

        Taxi(int x, int y, int move) {
            this.x = x;
            this.y = y;
            this.move = move;
        }
    }

    static class Passenger {
        int id, sx, sy, ex, ey;

        Passenger() { }
    }

    static int N, M, fuel;
    static int[][] arr = new int[21][21];
    static Taxi taxi;
    static Passenger taken = null;
    static Map<Integer, Passenger> passMap = new HashMap<>();

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

        // input
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        fuel = Integer.parseInt(st.nextToken());

        for (int i = 1; i < N + 1; i++) {
            st = new StringTokenizer(br.readLine());

            for (int j = 1; j < N + 1; j++) {
                arr[i][j] = Integer.parseInt(st.nextToken());
            }
        }

        st = new StringTokenizer(br.readLine());
        taxi = new Taxi(Integer.parseInt(st.nextToken()), Integer.parseInt(st.nextToken()), 0);

        for (int i = 0; i < M; i++) {
            st = new StringTokenizer(br.readLine());

            Passenger p = new Passenger();
            p.id =  i + 2;  // 벽이 1 이라서 2 부터 넘버링
            p.sx = Integer.parseInt(st.nextToken());
            p.sy = Integer.parseInt(st.nextToken());
            p.ex = Integer.parseInt(st.nextToken());
            p.ey = Integer.parseInt(st.nextToken());

            passMap.put(p.id, p);

            // 출발지는 겹치지 않기 때문에 맵에 기록
            arr[p.sx][p.sy] = p.id;
        }

        // solution
        solution();
    }

    // 모든 승객을 데려다 줄때까지 BFS 를 반복하며 연료의 양을 확인한다.
    static void solution() {
        while (!passMap.isEmpty()) {
            int toStartFuel = bfs();
            fuel -= toStartFuel;

            if (fuel < 0) break;

            int toDestFuel = bfs();
            fuel -= toDestFuel;

            if (fuel < 0) break;

            fuel += toDestFuel * 2;
        }

        System.out.println(fuel < 0 ? -1 : fuel);
    }

    static int bfs() {
        Queue<Taxi> q = new LinkedList<>();
        Queue<Passenger> candidates = new LinkedList<>();
        boolean[][] visited = new boolean[21][21];
        int[] dx = {-1, 1, 0, 0};
        int[] dy = {0, 0, -1, 1};
        
        int prevMove = taxi.move;
        visited[taxi.x][taxi.y] = true;
        q.add(taxi);

        while (!q.isEmpty()) {
            Taxi now = q.poll();

            // 이동 중에 연료가 떨어지면 종료
            if (fuel - now.move < 0) {
                return Integer.MAX_VALUE;
            }

            // 택시 이동 시간대가 다르고 candidates 가 이미 존재하면 break
            if (prevMove != now.move && !candidates.isEmpty()) {
                break;
            }

            prevMove = now.move;

            if (taken == null) {
                // 택시가 비어있는 경우 가장 가까운 승객 후보를 만나면 candidates 에 추가
                int id = arr[now.x][now.y];

                if (id > 1) {
                    Passenger p = passMap.get(id);
                    candidates.add(p);
                }
            } else {
                // 택시에 승객이 있는 경우 도착지를 만나면 종료
                if (now.x == taken.ex && now.y == taken.ey) {
                    passMap.remove(taken.id);
                    taken = null;
                    taxi = new Taxi(now.x, now.y, 0);
                    return prevMove;
                }
            }

            // 동서남북 이동
            for (int i = 0 ; i < 4; i++) {
                int nx = now.x + dx[i];
                int ny = now.y + dy[i];

                if (0 < nx && nx < N+1 && 0 < ny && ny < N+1) {
                   if (arr[nx][ny] != 1 && visited[nx][ny] == false) {
                       q.add(new Taxi(nx, ny, now.move + 1));
                       visited[nx][ny] = true;
                   } 
                }
            }
        }

        // while 문이 끝났는데 candidates 에 아무것도 없으면 벽에 막혀서 도달못함
        if (candidates.isEmpty()) {
            return Integer.MAX_VALUE;
        }

        taken = findNearest(candidates);
        taxi = new Taxi(taken.sx, taken.sy, 0);
        arr[taken.sx][taken.sy] = 0;
        return prevMove;
    }

    // 같은 거리면 x 가 작고, y 가 작은 사람으로
    static Passenger findNearest(Queue<Passenger> q) {
        Passenger target = q.poll();

        while (!q.isEmpty()) {
            Passenger compare = q.poll();

            if (compare.sx < target.sx) {
                target = compare;
            } else if (compare.sx == target.sx && compare.sy < target.sy) {
                target = compare;
            }
        }

        return target;
    }
}


drop, dropLast, dropWhile, dropLastWhile

drop 은 문자열의 앞이나 뒷부분을 자를 때 사용된다.

내부적으로는 substring 으로 구현되어 있다.

  • drop : 앞에서부터 n 개의 문자를 제거한 String 을 반환
  • dropLast: 뒤에서부터 n 개의 문자를 제거
  • dropWhile, dropLastWhile: 조건을 만족하지 않는 문자가 나올때까지 제거


/* definition */
fun String.drop(n: Int): String

fun String.dropLast(n: Int): String

fun String.dropWhile(
  predicate: (Char) -> Boolean
): String

fun String.dropLastWhile(
  predicate: (Char) -> Boolean
): String


/* exmaple */
val string = "<<<First Grade>>>"

println(string.drop(6)) // st Grade>>>
println(string.dropLast(6)) // <<<First Gr
println(string.dropWhile { !it.isLetter() }) // First Grade>>>
println(string.dropLastWhile { !it.isLetter() }) // <<<First Grade


Reference

String

'Language > Kotlin' 카테고리의 다른 글

Kotlin Collections 와 Sequences 의 차이점 (feat. Java Stream)  (1) 2022.01.27
Kotlin Enum  (0) 2021.10.06
Kotlin Collection Sorting (정렬)  (0) 2021.02.19
[Kotlin] Swap  (0) 2020.11.11
[Kotlin] For 문  (0) 2020.11.11

Java Static 이란?

변수나 메소드 앞에 static 키워드를 붙여 사용한다.

static 은 같은 클래스에 있는 경우 같은 메모리 주소를 바라본다는 점을 이용해서 메모리 효율 최적화, 데이터 공유 등을 위해 사용한다.

한글로는 "정적인" 이라는 뜻인데 한번에 이해하기가 쉽지 않다.

동적인 다른 자원들과 달리 프로그램 상의 변하지 않는 자원이라고 생각하면 된다.


생성 시기와 소멸 시기

static 키워드를 사용하면 객체가 생성되는 시점이 아닌 Class Loader 가 클래스를 Load 할 때 Data 영역에 메모리가 할당되게 된다.

이 영역은 같은 유형의 클래스마다 공유되며 Process 가 종료되는 시점에서 해제되므로 static 키워드의 생명주기 역시 Class Load 시 생성되고 Process 종료 시 해제되게 된다.


static 변수

클래스 내에서 static 으로 선언된 변수는 모든 클래스 객체에서 같은 메모리 주소를 바라본다.

예를 들어 Student 라는 클래스가 있다. 이 클래스에는 학생들이 살고 있는 도시인 teacher 변수를 갖는다.

public class Student {
  String name;
  String teacher;
}

학생들은 모두 같은 반이라서 선생님이 같다고 가정하자.

만약 반의 선생님이 바뀌어도 모든 학생들이 동시에 바뀌어야 한다.

보통 한 반에 학생이 30 명이라고 가정하면, 선생님이 바뀌었을 때 30 명의 학생들을 전부 바꿔줘야 한다.

하지만 이럴 때 teacher 변수를 static 으로 선언한다면 Student 객체의 모든 teacher 가 같은 메모리를 공유해서 데이터를 바꾸면 동시에 바뀐다.


static 메소드

static 메소드는 객체 생성 없이 사용할 수 있으며 보통 유틸리티 클래스에서 주로 사용한다.

static 메소드 내에서는 static 변수만 사용 가능하고 this 호출이 불가능하다.

"이메일 형식 검사", "현재 시간 구하기" 등은 새로운 객체를 할당할 필요가 없기 때문에 static 으로 작성하는 게 좋다.


static 을 응용항 싱글톤 패턴 (Singleton pattern)

디자인 패턴 중 하나인 싱글톤 패턴은 static 의 개념을 이용한 패턴이다.

싱글톤은 단 하나의 객체만 생성하는 패턴이다.

싱글톤 클래스를 만드는 규칙은 다음과 같다.

  1. 싱글톤 클래스의 생성자를 private 로 하여 외부에서 생성하지 못하게 한다.
  2. 클래스 내부에 static 변수를 선언한다.
  3. getInstance() 메소드를 static 으로 만들고 내부에서 싱글턴 객체를 선언하여 리턴한다.
class Singleton {
  private static Singleton instance;

  // 생성자를 private 로 하여 외부에서 생성 불가능하게 만듬
  private Singleton() { }

  public static Singleton getInstacne() {
    if (instance == null) {
      instance = new Singleton();
    }

    return instance;
  }
}


Problem


nums 배열에 있는 각 숫자에 + 또는 - 중 하나를 선택해서 전부 계산할 경우 S 와 같은 값이 나오는 경우의 수는 몇 가지인지 찾는 문제입니다.



Solution

DFS 를 이용해서 모든 경우의 수를 찾아주면 됩니다.

index 를 하나씩 증가시키면서 nums[index] 를 더하는 경우, 빼는 경우를 차례대로 확인합니다.



Java Code

class Solution {
    public int findTargetSumWays(int[] nums, int S) {
        return dfs(nums, S, 0, 0);
    }
    
    private int dfs(int[] nums, int S, int index, int sum) {
        if (index == nums.length) {
            return S == sum ? 1 : 0;
        }
        
        return dfs(nums, S, index + 1, sum + nums[index]) +
               dfs(nums, S, index + 1, sum - nums[index]);
    }
}


Problem


Medium 문제입니다.

주어진 문자열 S 를 나누었을 때 각 파트는 다른 파트와 중복된 문자가 없어야 합니다.

예를 들어 a, b, c 세 파트로 나눈다면 a 파트에 존재하는 문자는 b, c 파트에 존재하지 않아야 합니다.

위 조건을 만족하면서 최대한 많은 파트로 쪼갰을 때 각각의 길이를 List 에 담아 리턴하면 됩니다.



Solution

for 문을 총 두번 돕니다.

첫 번째 for 문에서는 각 문자들이 마지막으로 등장하는 index 를 저장해둡니다.

두 번째 for 문에서는 현재 지나고 있는 문자들 중 가장 마지막에 나오는 문자의 index 를 저장해둔 뒤 i 가 그 index 에 도달했을 때, 더 이상 중복되는 문자는 나오지 않는다고 판단하여 길이를 저장합니다.


주어진 문자 "ababcbacadefegdehijhklij" 로 예를 들면

"ababcbaca" 문자열이 진행되는 동안 farRight 변수는 8 입니다.

마지막 'a' 를 만나는 순간 i == farRight 가 되고 지금까지 나온 문자들은 이후에는 절대 나오지 않는다 라는 걸 알 수 있습니다.

리스트에 start 부터 farRight 까지의 길이를 넣어두고 다음 문자부터 다시 위 과정을 반복하면 됩니다.



Java Code

class Solution {
    public List<Integer> partitionLabels(String S) {
        int[] lastIndexs = new int[26];

        for (int i = 0; i < S.length(); i++) {
            lastIndexs[S.charAt(i) - 'a'] = i;
        }
        
        List<Integer> list = new ArrayList<>();
        int start = 0;
        int farRight = 0;
        
        for (int i = 0; i < S.length(); i++) {
            farRight = Math.max(farRight, lastIndexs[S.charAt(i) - 'a']);
            
            if (i == farRight) {
                list.add(farRight - start + 1);
                start = i + 1;
            }
        }
        
        return list;
    }
}


Problem


Easy 문제입니다.

문자열 s 가 특정 substring 의 반복으로만 이루어진 경우를 찾는 문제입니다.



Solution

substring 을 구한 후 반복해서 붙인 다음 s 와 비교하면 됩니다.

순서대로 나열하면 다음과 같습니다.

  1. substring 의 길이를 s 의 절반부터 시작해서 1 까지 확인한다. (절반보다 길면 substring 이 반복될 수가 없음)
  2. s 의 길이가 substring 의 길이와 나누어 떨어지는 지 확인한다.
  3. substring 을 반복해서 붙인 다음 s 와 비교한다.
  4. 일치하는게 나올 때까지 확인한다.



Java Code

class Solution {
    public boolean repeatedSubstringPattern(String s) {
        for (int len = s.length() / 2; len > 0; len--) {
            if (s.length() % len != 0) {
                continue;
            }
            
            int repeatCount = s.length() / len;
            String pattern = s.substring(0, len);
            StringBuilder sb = new StringBuilder();
            
            while (repeatCount-- > 0) {
                sb.append(pattern);
            }
            
            if (sb.toString().equals(s)) {
                return true;
            }
        }
        
        return false;
    }
}


+ Recent posts