https://www.acmicpc.net/problem/1251

 

1251번: 단어 나누기

알파벳 소문자로 이루어진 단어를 가지고 아래와 같은 과정을 해 보려고 한다. 먼저 단어에서 임의의 두 부분을 골라서 단어를 쪼갠다. 즉, 주어진 단어를 세 개의 더 작은 단어로 나누는 것이다

www.acmicpc.net

 

 

[정답 코드]

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

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

        String input = br.readLine();

        String left = "";
        String mid = "";
        String right = "";

        ArrayList<String> list = new ArrayList<>();

        for (int i = 1; i < input.length() - 1; i++) {
            left = input.substring(0, i);
            for (int j = i + 1; j < input.length(); j++) {
                String tmp = new String();
                mid = input.substring(i, j);
                right = input.substring(j);
                tmp += backward(left);
                tmp += backward(mid);
                tmp += backward(right);
                list.add(tmp);
            }
        }
        Collections.sort(list);
        System.out.println(list.get(0));
    }
    static String backward(String word) {
        String tmp = "";
        for (int i = word.length() - 1; i >= 0; i--) {
            tmp += word.charAt(i);
        }
        return tmp;
    }
}

 

[알고리즘]

- 부르트포스 알고리즘

- 문자열

 

[풀이]

1. 이중 for문을 돌며 String.substring()을 활용해 left, mid, right의 3단어로 나눈다.

2. 문자열을 거꾸로 치환시키는 메서드 backward로 만들어 새 문자열을 만든다.

3. 새로 만든 문자열을 ArrayList에 추가한다.

4. Collections.sort() 메서드를 활용해 사전순으로 정렬한다.

5. list의 맨 앞 문자열을 출력한다.

 

이 방법이외에도 StringBuilder 를 활용하면 Stringbuilder에 자체적으로 문자열을 뒤집는 Stringbuilder.reverse()를 활용하면 더 간단하게 풀 수 있다.

'코딩테스트' 카테고리의 다른 글

백준 1543: 문서 검색[JAVA]  (0) 2024.04.09
백준 1436: 영화감독 숌[JAVA]  (0) 2024.04.09
백준 4179: 불![JAVA]  (0) 2024.04.04
백준 2110번: 공유기 설치[JAVA]  (1) 2024.04.04
백준 1987번: 알파벳[JAVA]  (1) 2024.04.03

https://www.acmicpc.net/problem/1920

 

1920번: 수 찾기

첫째 줄에 자연수 N(1 ≤ N ≤ 100,000)이 주어진다. 다음 줄에는 N개의 정수 A[1], A[2], …, A[N]이 주어진다. 다음 줄에는 M(1 ≤ M ≤ 100,000)이 주어진다. 다음 줄에는 M개의 수들이 주어지는데, 이 수들

www.acmicpc.net

[시간초과가 발생한 코드]

더보기
import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");

        int N = Integer.parseInt(st.nextToken());
        st = new StringTokenizer(br.readLine(), " ");
        // ArrayList에 값을 담아줌
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 0; i < N; i++) {
            list.add(Integer.parseInt(st.nextToken()));
        }

        int M = Integer.parseInt(br.readLine());
        st = new StringTokenizer(br.readLine(), " ");
        for (int i = 0; i < M; i++) {
            if (list.contains(Integer.parseInt(st.nextToken()))) { // list.contains() 함수를 사용
                bw.write("1");
            } else {
                bw.write("0");
            }
            bw.write("\n");
        }
        bw.flush();
        bw.close();
        br.close();
    }
}

[정답코드]

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

public class Main {
    public static int[] arr;
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");

        int N = Integer.parseInt(st.nextToken());
        arr = new int[N];

        st = new StringTokenizer(br.readLine(), " ");
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 0; i < N; i++) {
            arr[i] = Integer.parseInt(st.nextToken());
        }

        // 이분탐색은 반드시 정렬이 되어야만 한다.
        Arrays.sort(arr);

        int M = Integer.parseInt(br.readLine());
        st = new StringTokenizer(br.readLine(), " ");
        for (int i = 0; i < M; i++) {
            if (binarySearch(Integer.parseInt(st.nextToken())) >= 0) {
                bw.write("1");
            } else {
                bw.write("0");
            }
            bw.write("\n");
        }
        bw.flush();
        bw.close();
        br.close();
    }

    public static int binarySearch(int key) {
        int low = 0; // 탐색 범위의 왼쪽 끝 인덱스
        int high = arr.length - 1; // 탐색 범위의 오른쪽 끝 인덱스

        // low 가 high 보다 커지면 종료
        while (low <= high) {
            int mid = (low + high) / 2; // 찾는 범위의 중간 값

            if (key < arr[mid]) { // key 가 중간값보다 작을 경우
                high = mid - 1;
            } else if (key > arr[mid]) { // key 가 중간값보다 클 경우
                low = mid + 1;
            } else { // key 가 중간값과 같을 경우
                return mid;
            }
        }
        // 끝까지 탐색했지만 값이 존재하지 않을 경우
        return -1;
    }
}

 

[설명]

이 문제는 이분 탐색을 활용하면 풀 수 있는문제다. 하지만 처음에 봤을때 간단하게 보여서 list.contains() 메서드를 활용하면 풀릴것같아서 시도해보았다. 결과는 시간초과가 발생했다. 

contains 메서드의 설명을 보면 알 수 있듯이 contains 메서드도 결국에는 리스트의 처음부터 끝까지 탐색하기 때문에 내가 직접 반복문을 시행하나 메서드를 활용하나 별 차이가 없기 때문에 시간초과가 발생했다.

그래서 이분탐색을 활용해 다시 정답을 구해냈다. 코드는 간단해서 설명할것이 없을것 같고 나는 시간복잡도가 궁금해 자료를 검색해봤다.

이러한 일반적인 이분탐색은 거의 대다수가 O(logN) 의 시간 복잡도를 가진다.

N개의 요소를 가진 리스트를 K번 반복한다는 함수를 구한다면 다음과 같다.

운이 좋아 1번만에 찾는다면 (1/2)*N 가 될것이고 3번만에 찾는다면 (1/2)*(1/2)*(1/2)*N 이 될것이다.

운이 안좋아 최악의 경우로  마지막 탐색범위가 1인 경우가 있을수도있다. K번 반복 했을 때 쪼개진 크기가 1이라고 가정한다면, 결국  K번 반복한다고했으니 이를 다음과같이 풀어볼 수 있다.

결국 시간복잡도는 O(logN) 이 되는것이다.

원래 내가 작성했던 코드는 간단히 보자면 이중for문이 되는것이기 때문에 시간복잡도는 O(N^2) 혹은 O(NM)이 된다.

https://www.acmicpc.net/problem/20920

 

20920번: 영단어 암기는 괴로워

첫째 줄에는 영어 지문에 나오는 단어의 개수 $N$과 외울 단어의 길이 기준이 되는 $M$이 공백으로 구분되어 주어진다. ($1 \leq N \leq 100\,000$, $1 \leq M \leq 10$) 둘째 줄부터 $N+1$번째 줄까지 외울 단

www.acmicpc.net

 

 

[정답 코드]

import java.io.*;
import java.math.BigInteger;
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");

        int N = Integer.parseInt(st.nextToken());
        int M = Integer.parseInt(st.nextToken());
        Map<String, Integer> map = new HashMap<>();
        for (int i = 0; i < N; i++) {
            String word = br.readLine();
            if (word.length() >= M) { // 길이 M 이상만 저장
                map.put(word, map.getOrDefault(word, 0) + 1); // 같은 단어가 등록될경우 value를 하나씩 증가
            }
        }
        // map 의 value 기준 내림차순 정렬
        List<Map.Entry<String, Integer>> entryList = new LinkedList<>(map.entrySet());
        entryList.sort(new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                int cmp = o2.getValue().compareTo(o1.getValue()); // value 기준 내림차순 정렬 0일경우 value가 같음
                if (cmp == 0) {
                    int lengthCompare = Integer.compare(o2.getKey().length(), o1.getKey().length()); // value가 같을 경우 value가 같은것 끼리 key의 길이를 내림차순 정렬
                    return lengthCompare == 0 ? o1.getKey().compareTo(o2.getKey()) : lengthCompare; // key의 길이가 같을경우(lengthCompare == 0일경우) 길이가 같은것은 사전순으로 정렬
                }
                return cmp;
            }
        });
        for (Map.Entry<String, Integer> entry : entryList) {
            bw.write(entry.getKey());
            bw.write("\n");
        }
        bw.flush();
        bw.close();
        br.close();
    }
}

 

[설명]

이 문제는 맵을 활용한 키와 밸류의 정렬, Comparator를 잘 활용해야하는 문제이다.

먼저 단어를 입력받을때 단어의 길이가 M 이상인 경우만 Map에 저장해준다. 같은 단어가 나올때 해당 단어의 갯수를 map으로 카운팅 해준다.

입력이 끝났다면 map의 Value를 기준으로 내림차순 정렬을 해준다. 그렇다면 

        // map 의 value 기준 내림차순 정렬
        List<Map.Entry<String, Integer>> entryList = new LinkedList<>(map.entrySet());
        entryList.sort(new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                int cmp = o2.getValue().compareTo(o1.getValue()); // value 기준 내림차순 정렬 0일경우 value가 같음
                return cmp;
            }
        });

이런식으로 정렬을 해주면 된다.

 

하지만 문제에서 value 가 같을 경우에는 길이가 긴순으로 재 정렬을 해줘야한다고 되어있다. 그래서 약간 변형해

        // map 의 value 기준 내림차순 정렬
        List<Map.Entry<String, Integer>> entryList = new LinkedList<>(map.entrySet());
        entryList.sort(new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                int cmp = o2.getValue().compareTo(o1.getValue()); // value 기준 내림차순 정렬 0일경우 value가 같음
                if (cmp == 0) {
                    int lengthCompare = Integer.compare(o2.getKey().length(), o1.getKey().length()); // value가 같을 경우 value가 같은것 끼리 key의 길이를 내림차순 정렬
                    return lengthCompare; 
                }
                return cmp;
            }
        });

 cmp 가 같을 경우에 조건문을 걸어 길이가 긴순으로 내림차순을 해준다.

 

하지만 문제에 또다른 조건이 있다. 길이마저 같을 경우에는 사전순으로 정렬을 해준다고 되어있다.

        // map 의 value 기준 내림차순 정렬
        List<Map.Entry<String, Integer>> entryList = new LinkedList<>(map.entrySet());
        entryList.sort(new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                int cmp = o2.getValue().compareTo(o1.getValue()); // value 기준 내림차순 정렬 0일경우 value가 같음
                if (cmp == 0) {
                    int lengthCompare = Integer.compare(o2.getKey().length(), o1.getKey().length()); // value가 같을 경우 value가 같은것 끼리 key의 길이를 내림차순 정렬
                    return lengthCompare == 0 ? o1.getKey().compareTo(o2.getKey()) : lengthCompare; // key의 길이가 같을경우(lengthCompare == 0일경우) 길이가 같은것은 사전순으로 정렬
                }
                return cmp;
            }
        });

3항연산자를 사용해 lengthComapre가 같을경우에는 o1.getKey().compareTo(o2.getKey()) 로 비교해사전순으로 정렬해준다. 

그리고 마지막 출력을 할 때

        for (Map.Entry<String, Integer> entry : entryList) {
        	System.out.println(entry.getKey());
        }

이런식으로 출력을 할 경우 시간초과가 발생한다. 문제설명 맨 마지막에 설명을 보듯 BufferedWriter를 사용해주어야 시간초과가 발생하지 않는다.

 

BufferedWriter를 사용해 다시 코드를 작성한다면

        for (Map.Entry<String, Integer> entry : entryList) {
            bw.write(entry.getKey());
            bw.write("\n");
        }
        bw.flush();
        bw.close();
        br.close();

이런식으로 출력해 주어야 한다.

+ Recent posts