문자열은 불변
const s = 'abc';
s[0] = 'z'; // 무시되거나 엄격 모드에서 오류에 가깝게 동작하지 않음
const t = 'z' + s.slice(1); // 'zbc'
바꾸려면 새 문자열을 만듭니다. 배열로 쪼개 수정한 뒤 join하는 패턴이 흔합니다.
const chars = [...s]; // 유니코드 주의: 이모지는 코드유닛이 갈라질 수 있음
chars.reverse();
return chars.join('');
한글·BMP 안 문자에는 대체로 [...s]가 무난합니다.
파싱 패턴
const lines = input.trim().split(/\n/);
const nums = lines[0].split(/\s+/).map(Number);
const [n, ...rest] = nums;
정규식 /\s+/는 공백·탭이 여러 개여도 토큰을 나눕니다.
slice vs splice
arr.slice(i, j): 복사본, 원본 유지, 끝 미포함arr.splice(i, deleteCount, ...items): 원본 수정
const mid = arr.slice(1, -1);
검색
s.includes('ab');
s.indexOf('ab'); // 없으면 -1
s.startsWith('ab');
s.endsWith('ab');
arr.includes(x);
정렬
arr.sort((a, b) => a - b); // 숫자 오름차순
arr.sort((a, b) => a.localeCompare(b)); // 문자열
기본 sort()는 문자열 기준이라 [10, 2] → [10, 2]가 아니라 ['10','2'] 식으로 意外한 결과가 납니다. 숫자면 비교 함수 필수.
빈도 세기 (맛보기)
const freq = new Map();
for (const ch of s) {
freq.set(ch, (freq.get(ch) || 0) + 1);
}
해시는 알고리즘 원리에서 더 깊게 다룹니다.
투포인터와 문자열
회문 검사:
function isPal(s) {
let i = 0, j = s.length - 1;
while (i < j) {
if (s[i] !== s[j]) return false;
i++; j--;
}
return true;
}
부분 문자열
s.slice(i, j) 끝 미포함. s.substring은 인자를 뒤바꾸기도 해서 slice를 권합니다.
성능 감각
큰 루프에서 s += ch를 반복하면 느릴 수 있습니다.
배열에 push 후 join('')이 safer한 경우가 많습니다.