[JAVA 알고리즘]BAEKJOON 1897번 토달기
토달기
문제 설명
희현이는 원장선생님 말씀에 토를 다는 것을 몹시 좋아한다. 토를 단다는 것은 원장선생님께서 어떤 단어를 말씀하시면 그 단어의 맨 앞이나 중간, 혹은 맨 뒤에 한 글자를 끼워 넣어서 새로운 단어를 만드는 것으로, 버릇없는 행동과는 아무런 관계가 없는 순수한 단어 놀이이다.
희현이는 사전에 등재된 단어만을 사용한다. 사전은 d개의 단어로 구성되어 있으며, 각 단어는 80자 이내의 알파벳 소문자로만 이루어져 있다. 희현이는 원장선생님께서 어떤 단어를 말씀하셨을 때, 한 글자씩 토를 달아 사전에 등재된 단어를 계속 만들어 갈 경우, 가장 긴 단어를 만들려면 어떻게 해야 하는지가 궁금해졌다. 이를 해결하는 프로그램을 작성하라.
입력
첫 줄에 사전에 등재된 단어의 수 d와, 원장님이 처음 말씀하신 단어가 주어진다. (1 ≤ d ≤ 1,000) 원장님이 처음 말씀하신 단어의 길이는 세 글자이며, 사전에 있는 단어를 말씀하셨다. 다음 d개의 줄에는 사전에 등재된 단어가 한 줄에 하나씩 주어진다.
출력
첫 줄에 토달기 규칙을 지키며 단어를 만들어 갈 때, 만들 수 있는 단어 중 가장 긴 것을 출력한다. 답이 여럿일 경우 어느 것이나 상관없다.
제한사항
- 없음
입출력 예
input | return |
9 cal cal calf calfs call calls choral chorale coal coral |
chorale |
문제 분석
새로운 단어를 만드는 규칙은 2가지이다.
- 맨 앞이나 맨 뒤에 한 글자를 넣어 전의 단어가 그대로 쓰이는 경우
- 단어 중간에 한 글자가 끼워지는 경우
단어의 길이가 순차적으로 늘어나기 때문에 주어진 단어를 오름차순 정렬을 시켜하는 것이 편해 보인다.
1번의 경우는 문자를 포함하는지 확인하면 알 수 있다.
2번의 경우가 복잡해지는데, 해당 단어를 하나씩 그전 단어와 체크를 해서 다른 곳이 한 곳인 인지를 보고 확인을 할 수 있다.
한 글자씩 확인할 때 순서의 중요한 점
똑같은 알파뱃의 종류이지만 순서로 인해 규칙에 안 맞을 수 있다.
c a l l -> c a e l l (가능)
c a l l -> c l a e l (불가능)
손으로 풀기
- 입력값을 담을 배열과 중복을 방지할 사용 여부를 확인할 배열을 만든다.
- 순서대로 확인을 하기 위해 입력값을 담은 배열을 오름차순으로 정렬한다.
- BFS 방식으로 문자 출력
- 큐가 빌 때까지 반복하면서 가장 긴 단어까지 반복한다.
- 현재 단어보다 한자리 긴 단어 중에 사용하지 않는 단어일 때,
- 단어를 포함하면 큐에 넣어준다.
- 한 글자씩 비교해 틀린 부분이 한 곳이면 큐에 넣어준다.
- 큐에서 마지막에 나온 단어를 출력한다.
- DFS 방식으로 문자 출력
- 현재 단어보다 한자리 긴 단어중에 사용하지 않는 단어일 때,
- 단어를 포함하면 단어를 재귀함수로 반복한다.
- 한 글자씩 비교해 틀린 부분이 한 곳이면 단어를 재귀함수로 반복한다.
- 반복을 하면서 단어의 길이를 비교해 가장 긴 단어를 출력한다.
나의 문제풀이
import java.io.*;
import java.util.*;
public class Main {
static String[] words;
static boolean[] visited;
static String word = "";
public static void main(String[] args) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer stringTokenizer = new StringTokenizer(bufferedReader.readLine());
int d = Integer.parseInt(stringTokenizer.nextToken());
String keyword = stringTokenizer.nextToken();
words = new String[d];
visited = new boolean[d];
for (int i = 0; i < d; i++) {
words[i] = bufferedReader.readLine();
}
Arrays.sort(words, (o1, o2) -> o1.length() - o2.length());
visited[0] = true;
// BFS(keyword);
DFS(keyword);
System.out.println(word);
}
public static void BFS(String keyword) {
Queue<String> queue = new LinkedList<>();
queue.offer(keyword);
while (!queue.isEmpty()) {
word = queue.poll();
for (int i = 1; i < words.length; i++) {
// 다음 크기의 단어인지 확인을 한다.
if (!visited[i] && word.length() + 1 == words[i].length()) {
// 한 글자가 앞, 뒤에 붙었을 경우 전 단어가 그대로 포함 된다.
if (words[i].contains(word)) {
queue.offer(words[i]);
visited[i] = true;
continue;
}
// 한 글자가 중간에 들어 갔을 경우 한 글자씩 순서에 맞게 확인을 해본다.
char[] beforeWord = word.toCharArray();
char[] afterWord = words[i].toCharArray();
boolean check = false;
for (int j = 0, k = 0; j < afterWord.length; j++) {
if (beforeWord[k] == afterWord[j]) {
k++;
continue;
}
if (!check) {
check = true;
continue;
}
if (check) {
check = false;
break;
}
}
// true로 나오면 1번만 다른 단어 false로 나오면 2번 이상 다른 단어
if (check) {
queue.offer(words[i]);
visited[i] = true;
}
}
}
}
}
public static void DFS(String keyword) {
if (word.length() < keyword.length()) {
word = keyword;
}
for (int i = 1; i < words.length; i++) {
// 다음 크기의 단어인지 확인을 한다.
if (!visited[i] && keyword.length() + 1 == words[i].length()) {
// 한 글자가 앞, 뒤에 붙었을 경우 전 단어가 그대로 포함 된다.
if (words[i].contains(keyword)) {
visited[i] = true;
DFS(words[i]);
continue;
}
// 한 글자가 중간에 들어 갔을 경우 한 글자씩 순서에 맞게 확인을 해본다.
char[] beforeWord = keyword.toCharArray();
char[] afterWord = words[i].toCharArray();
boolean check = false;
for (int j = 0, k = 0; j < afterWord.length; j++) {
if (beforeWord[k] == afterWord[j]) {
k++;
continue;
}
if (!check) {
check = true;
continue;
}
if (check) {
check = false;
break;
}
}
// true로 나오면 1번만 다른 단어 false로 나오면 2번 이상 다른 단어
if (check) {
visited[i] = true;
DFS(words[i]);
}
}
}
}
}
처음에 조건을 이전 문자와 다음 문자를 비교할 때, 그냥 한 글자를 가지고 있는 지를 확인을 하면서 그 글자를 제외하면서 가능여부를 확인을 하는 로직으로 만들어서 틀리게 나와 무슨 문제인지를 인지를 못하고 있다가 아예 풀이를 중단하고 쉬다가 다시 처음부터 문제를 분석하고 풀이를 하니 문자를 비교할 때 순서가 중요한 게 이제야 보였다.
문제를 풀면서 한 방법으로 너무 생각을 집중하면 간단히 보일게 안 보이게 될 수 있어 이점을 조심하고 풀리지 않는다면 처음부터 다시 생각을 해 환기 좀 시키기도 해야겠다.
알고리즘 분류
- 자료 구조
- 그래프 이론
- 그래프 탐색
- 너비 우선 탐색
- 문자열
- 해시를 사용한 집합과 맵
문제 출처 - https://www.acmicpc.net/problem/1897
1897번: 토달기
첫 줄에 사전에 등재된 단어의 수 d와, 원장님이 처음 말씀하신 단어가 주어진다. (1 ≤ d ≤ 1,000) 원장님이 처음 말씀하신 단어의 길이는 세 글자이며, 사전에 있는 단어를 말씀하셨다. 다음 d개
www.acmicpc.net