N
단어들
두 단어의 비슷한 정도 = 공통 접두사의 길이
접두사: 두 단어의 맨 앞에서부터 연속으로 같은 문자열
예:
"AHEHHEH" vs "AHAHEH" → 공통 접두사 "AH" → 길이 2"AB" vs "CD" → 공통 접두사 없음 → 길이 0우리가 해야 할 일:
N개의 단어 중에서, 공통 접두사 길이가 가장 긴 두 단어를 찾아 출력
(단, 두 단어는 서로 달라야 한다)
접두사 최대 길이가 같은 쌍이 여러 개 있으면,
0번: apple
1번: apply
2번: banana
이 중 apple과 apply의 공통 접두사는 "appl"일 때,
두 단어의 인덱스는 (0, 1)
그래서 S = apple
T = apply
0번 단어: abcxxxx
1번 단어: abczzzz
2번 단어: abcqqqq
그럼 접두사(adc)는 3단어가 겹치는데,
이론적으로 만들 수 있는 쌍은:
(0,1)
(0,2)
(1,2)
이렇게 3개이다.
그런데 문제의 우선순위 규칙은:
<첫번째 조건>
먼저 “앞에 오는 단어(S)”가 가장 작은 인덱스여야 한다 → 즉 S=0
<두번째 조건>
S가 같다면 “뒤에 오는 단어(T)”가 가장 작은 인덱스여야 한다 → 즉 T=1
그래서 정답 쌍은 무조건 (0,1)이 된다.
왜냐하면 T(두 번째 단어)의 인덱스가 1보다 뒤에 있으니 (0,2)는 (0,1)에 밀린다.
즉, (i, j) 쌍을 고를 때
i(앞 단어 인덱스)가 가장 작은 쌍i도 같다면, j가 가장 작은 쌍을 고르면 된다. (이거 이해하느라 좀 시간걸림)
단어들을 입력받아 배열 words에 저장
모든 단어의 최대 길이 maxLen을 구함
접두사 길이 K를 maxLen부터 1까지 줄여가며,
K 이상인 단어들의 앞 K글자 접두사를 기준으로특정 K에서,
만약 어떤 K에서도 두 단어 이상 겹치는 접두사가 없다면
→ 공통 접두사 길이 0이 최대이므로 (0번 단어, 1번 단어) 출력
우리는 공통 접두사 길이가 최대인 쌍을 원함
그렇다면
K = maxLen 에서 가능한 쌍이 있는지 확인K = maxLen - 1, …, K = 1 순서로 내려오다가그래서 “큰 K → 작은 K 순서”로 탐색하고,
처음 성공하는 순간 바로 종료하면 된다.
K를 하나 정했다고 생각하고, 그 K에 대해 하는 일을 자세히 보면:
map:
int[2] 배열 = {해당 접두사를 가진 가장 앞에 입력된 단어 인덱스, 두 번째로 앞에 입력된 단어 인덱스}모든 단어 i(0 ~ N-1)에 대해:
단어 길이가 K 미만이면 스킵
접두사 pref = words[i].substring(0, K) 추출
map에서 pref에 해당하는 pair 찾거나 새로 생성
pair 갱신 규칙
pair[0] = 가장 작은 인덱스pair[1] = 그 다음으로 작은 인덱스모든 단어를 처리한 후,
map의 각 pair를 돌며
pair[1] != -1 인 것만 (두 단어 이상 존재하는 접두사 그룹)
그들 중에서
pair[0]이 가장 작은 것,pair[1]이 가장 작은 것을 candidate로 선택candidateFirst != -1 이라면
→ 이 K에서 답이 존재 → 이 K가 최대 접두사 길이 → 정답 확정 후 반복 종료
int N = Integer.parseInt(br.readLine().trim());
String[] words = new String[N];
int maxLen = 0;
for (int i = 0; i < N; i++) {
words[i] = br.readLine().trim();
maxLen = Math.max(maxLen, words[i].length());
}
N : 단어 개수words : 단어들을 저장하는 배열maxLen : 등장하는 단어 중 가장 긴 길이 (접두사 최대 길이 상한)int bestFirst = -1; // 정답 S의 인덱스
int bestSecond = -1; // 정답 T의 인덱스
int bestLen = 0; // 최대 공통 접두사 길이
for (int K = maxLen; K >= 1; K--) {
HashMap<String, int[]> map = new HashMap<>();
K마다 새 map을 사용K 사이클에서만 의미 있는 그룹들이기 때문for (int i = 0; i < N; i++) {
if (words[i].length() < K) continue;
String pref = words[i].substring(0, K);
int[] pair = map.get(pref);
if (pair == null) {
pair = new int[]{i, -1};
map.put(pref, pair);
} else {
int first = pair[0];
int second = pair[1];
if (i == first) {
continue;
}
if (i < first) {
pair[1] = first;
pair[0] = i;
} else if (i > first && (second == -1 || i < second)) {
pair[1] = i;
}
}
}
<깨우친 사실>
map의 value인 pair는 항상
pair[0] < pair[1] (둘 다 인덱스)갱신 로직 자세히:
처음 본 접두사면 {현재 인덱스, -1} 로 저장
이미 있는 접두사면:
i < first → 현재 i가 제일 앞이니까:
first 를 second로 밀어내고first를 i로 교체i > first이면서
second가 아직 없거나 (-1)i < second → 두 번째로 앞선 인덱스로 갱신이렇게 하면 접두사별로 늘 앞에서 두 개만 잘 유지할 수 있음.
int candidateFirst = -1;
int candidateSecond = -1;
for (int[] pair : map.values()) {
if (pair[1] == -1) continue; // 한 단어만 있으면 쌍이 안 됨
int f = pair[0];
int s = pair[1];
if (candidateFirst == -1 ||
f < candidateFirst ||
(f == candidateFirst && s < candidateSecond)) {
candidateFirst = f;
candidateSecond = s;
}
}
이 K에서 가능한 모든 접두사 그룹을 돌며
“입력 순서 상 앞에 오는 쌍”을 찾는 로직:
candidateFirst == -1) → 바로 채택f가 기존 후보의 candidateFirst보다 앞이면 → 교체f가 같다면, s가 더 앞이면 → 교체→ 문제에서 요구하는 우선순위 조건을 그대로 구현한 부분.
if (candidateFirst != -1) {
bestLen = K;
bestFirst = candidateFirst;
bestSecond = candidateSecond;
break;
}
if (bestFirst == -1) {
bestFirst = 0;
bestSecond = 1;
}
bw.write(words[bestFirst]);
bw.newLine();
bw.write(words[bestSecond]);
bw.newLine();
bw.flush();
접두사 길이를 기준으로 이분 탐색하는 것이 아니라,
길이가 작은 상수(≤ 100)라는 점을 이용해서
K = maxLen ... 1 까지 순회각 K에 대해
HashMap<String, int[]>로 “K 길이 접두사가 같은 단어들”을 빠르게 그룹화 후 완전탐색시간 복잡도(대략):
K 100개 × 단어 20,000개 × 접두사 잘라내기 O(K)L(단어 길이)가 매우 작다 (≤ 100)
→ 접두사 길이 K를 일일이 내려가며 확인해도 큰 문제가 안 됨
문제의 핵심은 “접두사가 같은 단어들만 비교하면 된다”는 점
→ 모든 쌍 i, j를 전부 비교하면 O(N²L)이지만,
→ 동일 접두사 그룹 안에서만 “앞에서 두 개”를 관리하면
중복 계산을 많이 줄일 수 있음
우선순위 규칙(입력 순서)을 index 로 자연스럽게 처리
pair[0], pair[1]에 “가장 앞 / 두 번째로 앞 단어”만 저장하면사용자 코드 그대로 두고, 의미를 붙여 다시 보여줄게요.
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));
int N = Integer.parseInt(br.readLine().trim());
// 1. 단어 입력 및 최대 길이 계산
String[] words = new String[N];
int maxLen = 0;
for (int i = 0; i < N; i++) {
words[i] = br.readLine().trim();
maxLen = Math.max(maxLen, words[i].length());
}
// 2. 최종 정답 인덱스, 길이
int bestFirst = -1; // 정답 S의 인덱스
int bestSecond = -1; // 정답 T의 인덱스
int bestLen = 0; // 최대 접두사 길이
// 3. 접두사 길이 K를 큰 것부터 줄여가며 탐색
for (int K = maxLen; K >= 1; K--) {
HashMap<String, int[]> map = new HashMap<>();
// 3-1. 길이 K의 접두사로 그룹화
for (int i = 0; i < N; i++) {
if (words[i].length() < K) continue;
String pref = words[i].substring(0, K);
int[] pair = map.get(pref);
if (pair == null) {
pair = new int[]{i, -1};
map.put(pref, pair);
} else {
int first = pair[0];
int second = pair[1];
if (i == first) {
continue;
}
if (i < first) {
pair[1] = first;
pair[0] = i;
} else if (i > first && (second == -1 || i < second)) {
pair[1] = i;
}
}
}
// 3-2. 이 K에서 조건을 만족하는 가장 앞쪽 쌍 찾기
int candidateFirst = -1;
int candidateSecond = -1;
for (int[] pair : map.values()) {
if (pair[1] == -1) continue; // 단어가 한 개뿐인 그룹 제외
int f = pair[0];
int s = pair[1];
if (candidateFirst == -1 ||
f < candidateFirst ||
(f == candidateFirst && s < candidateSecond)) {
candidateFirst = f;
candidateSecond = s;
}
}
// 3-3. 이 K에서 하나라도 쌍이 있으면 이게 최대 접두사 길이
if (candidateFirst != -1) {
bestLen = K;
bestFirst = candidateFirst;
bestSecond = candidateSecond;
break;
}
}
// 4. 어떤 K에서도 쌍을 못 찾으면 (공통 접두사 길이 0이 최대)
if (bestFirst == -1) {
bestFirst = 0;
bestSecond = 1;
}
// 5. 출력
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
bw.write(words[bestFirst]);
bw.newLine();
bw.write(words[bestSecond]);
bw.newLine();
bw.flush();
}
}
pair 갱신할 때
pair[0]와 pair[1]의 인덱스 순서(작은 것, 그 다음 것)를 항상 유지해야 함candidateFirst, candidateSecond 비교할 때
조건문에서 우선순위를 정확히 지켜야 함:
f 비교 → 같을 때 s 비교bestFirst == -1일 때 fallback 처리