이번 글에서는 첫 번째 인수로 문자열을, 두 번째 인수로 숫자 n, 세 번째 인수로 문자 c를 받는 JavaScript 함수를 작성해 보겠습니다. 이 함수는 문자열에 등장하는 모든 문자의 n번째 출현을 세 번째 인수로 전달받은 문자로 교체한 새 문자열을 반환해야 합니다.
예를 들어 'This is a sample string'에서 각 문자의 두 번째 출현을 '*'로 바꾸면 결과는 'This ***a s*mple string'이 됩니다. 두 번째로 등장하는 'i', 's', 공백, 'a'가 모두 '*'로 대체되는 것을 확인할 수 있습니다.
접근 방식
문자열을 한 글자씩 순회하면서 Map 객체에 각 문자가 지금까지 몇 번 등장했는지 기록하는 방식으로 문제를 해결할 수 있습니다. 특정 문자의 등장 횟수가 정확히 n에 도달하는 시점에 해당 자리를 지정 문자로 교체하고, 그 외의 경우에는 원래 문자를 그대로 이어 붙입니다.
예제 코드
다음은 실제 구현 코드입니다.
const str = 'This is a sample string';
const num = 2;
const char = '*';
const replaceNthAppearance = (str, num, char) => {
const creds = str.split('').reduce((acc, val, ind, arr) => {
let { res, map } = acc;
if(!map.has(val)){
map.set(val, 1);
if(num === 0){
res += char;
}else{
res += val;
}
}else{
const freq = map.get(val);
if(num - freq === 1){
res += char;
}else{
res += val;
};
map.set(val, freq+1);
};
return { res, map };
}, {
res: '',
map: new Map()
});
return creds.res;
}
console.log(replaceNthAppearance(str, num, char));
실행 결과
콘솔에는 다음과 같이 출력됩니다.
This ***a s*mple string
코드 동작 원리
- 문자열 분리 및 누적: split('')으로 문자열을 개별 문자 배열로 만든 뒤, reduce()를 사용해 하나의 결과 객체로 누적 처리합니다.
- 누적 객체(acc): 최종 결과 문자열(res)과 각 문자의 출현 빈도를 추적하는 Map(map) 두 가지 값을 유지합니다.
- 처음 등장한 문자: Map에 존재하지 않는 문자라면 빈도를 1로 설정하고 원본 문자를 그대로 추가합니다.
- n번째 출현 판별: 이미 등장한 문자라면 현재 빈도(freq)를 확인하여, num - freq === 1이면 이번이 n번째 출현이므로 지정 문자로 교체합니다. 그렇지 않다면 원본 문자를 추가한 후 빈도를 1 증가시킵니다.
이 알고리즘은 문자열을 단 한 번만 순회하므로 시간 복잡도가 O(n)으로 효율적이며, 공백을 포함한 모든 문자에 동일한 규칙이 적용된다는 점도 참고하시기 바랍니다.