|
| 1 | +/* |
| 2 | +1. 문제 이해 |
| 3 | +
|
| 4 | +입력으로 string s 와 int k 가 있고 s 에 대해 k번 만큼 글자를 바꿀 수 있다. |
| 5 | +이때 최대로 연속된 substring의 길이를 찾는 것이다. |
| 6 | +즉 k번만큼 바꿀 때 최대의 substring이 될 수 있도록 글자를 바꿔야 한다. |
| 7 | +
|
| 8 | +조건: s길이는 최대 10만, k는 최대 s길이 |
| 9 | +
|
| 10 | +여기서 한가지 포인트는 전체 길이 n 에서 가장 긴 동일 문자 길이 s 를 뺀 값인 n - s 가 k보다 작거나 같다면 이건 항상 전체 길이 만큼에 대해 동일 문자열로 변경할 수 있는것을 의미한다. |
| 11 | +왜냐하면 바꿔야할 문자 개수보다 바꿀 수 있는 개수가 더 많기 때문이다. |
| 12 | +
|
| 13 | +2. 알고리즘 |
| 14 | +
|
| 15 | +substring인걸 보니 다이나믹 프로그래밍이 생각나는데 어떻게 적용시켜야할지 고민이다. |
| 16 | +작은 문제를 어떻게 정의해야할까 ? |
| 17 | +
|
| 18 | +만약 dp가 아니라면? |
| 19 | +
|
| 20 | +정답 참고 -> 슬라이딩 윈도우 기법을 사용한다 |
| 21 | +2개의 포인터를 동일한 인덱스에 놓고 종료 인덱스를 1칸씩 늘려가면서 다른 문자열이 나오는지 체킹한다 |
| 22 | +
|
| 23 | +3. 예외 |
| 24 | +
|
| 25 | +
|
| 26 | +4. 구현 |
| 27 | +
|
| 28 | +GPT 참고 |
| 29 | +
|
| 30 | +이걸 구현 하는 부분에서 특히 while문을 이해하기 어려웠다. |
| 31 | +
|
| 32 | +for문을 통해 end 포인터를 하나씩 증가시킨다 |
| 33 | +이를 통해 윈도우 크기를 증가시킨다 |
| 34 | +그리고 counter map에 해당 글자 count를 1 추가하여 넣는다 |
| 35 | +모든 글자 count들을 돌면서 최대 maxCount를 찾는다 |
| 36 | +
|
| 37 | +그리고 중요한 조건인 k번을 사용해서 현재 윈도우를 동일한 글자로 만들 수 있는지 찾아낸다. |
| 38 | +이때 while문의 (end - start + 1 - maxCount > k) 부분이 핵심이다. 가장 위에서 설명한 대로 현재 윈도우 길이 + 1 - maxCount 가 k보다 크다는 것은 변경해야할 길이가 변경할 수 있는 길이보다 크다는 것이므로 모두 동일한 글자로 만들 수 없다는 것이므로 윈도우 크기를 왼쪽에서 줄여나가야 한다. |
| 39 | +
|
| 40 | +이렇게 줄여나가다가 변경해야할 길이 = 변경할 수 있는 길이 가 되면 현재 start ~ end 윈도우에서 가질 수 있는 최대의 동일 글자 길이 조건을 만족시키므로 maxLen를 초기화 시켜준다. |
| 41 | +
|
| 42 | +현재 start ~ end 에서 최대 길이를 알아냈으므로 그 다음 end를 증가시키며 이 과정을 반복한다. |
| 43 | +
|
| 44 | +
|
| 45 | +GPT 면접식 답변 |
| 46 | +
|
| 47 | +🎤 ① 문제 핵심 정의부터 말한다 |
| 48 | +
|
| 49 | +“이 문제의 핵심은 |
| 50 | +연속된 부분 문자열을 하나 선택했을 때, |
| 51 | +최대 k번 문자 교체로 모두 같은 문자로 만들 수 있는지를 판단하는 것입니다.” |
| 52 | +
|
| 53 | +🎤 ② 판단 기준을 먼저 제시한다 (중요) |
| 54 | +
|
| 55 | +“어떤 부분 문자열의 길이를 n, |
| 56 | +그 안에서 가장 많이 등장한 문자의 개수를 s라고 하면, |
| 57 | +n - s ≤ k 인 경우에만 |
| 58 | +이 문자열은 k번 이내의 교체로 하나의 문자로 만들 수 있습니다.” |
| 59 | +
|
| 60 | +👉 여기서 면접관은 이미 ‘아, 본질 이해했구나’ 하고 체크함. |
| 61 | +
|
| 62 | +🎤 ③ 이 기준을 어떻게 효율적으로 찾는지 설명 |
| 63 | +
|
| 64 | +“이 조건을 만족하는 가장 긴 n을 찾기 위해 |
| 65 | +저는 슬라이딩 윈도우를 사용했습니다.” |
| 66 | +
|
| 67 | +“오른쪽 포인터를 이동시키며 윈도우를 확장하고, |
| 68 | +현재 윈도우 내 문자 빈도를 카운트합니다.” |
| 69 | +
|
| 70 | +🎤 ④ 윈도우 유지 조건 설명 (핵심 로직) |
| 71 | +
|
| 72 | +“현재 윈도우에서 |
| 73 | +(윈도우 길이 - 가장 많이 등장한 문자 수)가 |
| 74 | +k를 초과하면, |
| 75 | +해당 구간은 더 이상 유효하지 않으므로 |
| 76 | +왼쪽 포인터를 이동시켜 윈도우를 줄입니다.” |
| 77 | +
|
| 78 | +👉 이 문장이 바로 코드의 while 조건 설명이다. |
| 79 | +
|
| 80 | +🎤 ⑤ 정답 갱신 시점 설명 |
| 81 | +
|
| 82 | +“조건을 만족하는 동안에는 |
| 83 | +현재 윈도우 길이를 최대값으로 갱신하며 |
| 84 | +전체 탐색은 한 번만 이루어지므로 |
| 85 | +시간 복잡도는 O(N)입니다.” |
| 86 | +
|
| 87 | +*/ |
| 88 | + |
| 89 | +import java.util.*; |
| 90 | + |
| 91 | +class Solution { |
| 92 | + public int characterReplacement(String s, int k) { |
| 93 | + int maxLen = 0; |
| 94 | + Map<Character, Integer> counter = new HashMap<>(); |
| 95 | + |
| 96 | + int start = 0; |
| 97 | + |
| 98 | + for (int end = 0; end < s.length(); end++) { |
| 99 | + char c = s.charAt(end); |
| 100 | + counter.put(c, counter.getOrDefault(c, 0) + 1); |
| 101 | + |
| 102 | + int maxCount = 0; |
| 103 | + for (int count : counter.values()) { |
| 104 | + maxCount = Math.max(maxCount, count); |
| 105 | + } |
| 106 | + |
| 107 | + while (end - start + 1 - maxCount > k) { |
| 108 | + char leftChar = s.charAt(start); |
| 109 | + counter.put(leftChar, counter.get(leftChar) - 1); |
| 110 | + start++; |
| 111 | + } |
| 112 | + |
| 113 | + maxLen = Math.max(maxLen, end - start + 1); |
| 114 | + } |
| 115 | + |
| 116 | + return maxLen; |
| 117 | + } |
| 118 | +} |
0 commit comments