개념
문자열은 그냥 문자를 한 줄로 세워 놓은 배열이라고 보면 편해요.
문자 코드 변환은 알파벳을 배열 인덱스로 쓸 때 자주 나와요.
"code"는 c, o, d, e 네 칸짜리 배열이고, s[0]으로 첫 글자를 꺼내는 것도 배열이랑 똑같아요. 딱 하나 다른 점 — 이 배열은 칸을 못 고쳐요. 그래서 반복문에서 s += c로 글자를 이어붙이면 매번 배열 전체를 새로 복사하느라 O(n²)으로 느려져요. 이 함정만 조심하면 나머지는 배열 지식 그대로예요.두 언어 모두 문자열은 불변(immutable)이에요. 한 글자만 바꾸려 해도 새 문자열이 통째로 만들어져요. 이 사실 하나가 문자열 문제의 성능을 거의 결정해요.기본 도구
| 하고 싶은 것 | JavaScript | Python |
|---|---|---|
| 부분 잘라내기 | s.slice(a, b) | s[a:b] |
| 뒤집기 | [...s].reverse().join("") | s[::-1] |
| 나누기 | s.split(" ") | s.split() |
| 합치기 | arr.join("") | "".join(arr) |
| 문자 → 코드 | s.charCodeAt(i) | ord(s[i]) |
| 코드 → 문자 | String.fromCharCode(n) | chr(n) |
| 소문자로 | s.toLowerCase() | s.lower() |
ord(c) - ord("a")는 a를 0, z를 25로 만들어 주니까, 길이 26짜리 배열 하나로 빈도수를 셀 수 있어요.양끝에서 좁혀오기
팰린드롬처럼 앞뒤가 대칭인지 보는 문제는 양끝에 포인터를 두고 가운데로 좁혀오면 뒤집은 문자열을 새로 만들 필요가 없어요. 추가 메모리 없이O(n)이에요. 8주차 투 포인터의 예고편이에요."abcba"가 회문인지 손으로 검사해 볼게요. 왼쪽 포인터 i와 오른쪽 포인터 j를 양끝에 두고, 두 글자가 같으면 안쪽으로 한 칸씩 좁혀요. 서로 지나칠 때까지 한 번도 안 어긋나면 회문이에요.포인터가 가운데서 만나면("abcba" 회문 검사 — i는 오른쪽으로, j는 왼쪽으로인덱스 0 1 2 3 4문자 a b c b ai=0, j=4 : s[0]=a s[4]=a 같음 → i++, j--i=1, j=3 : s[1]=b s[3]=b 같음 → i++, j--i=2, j=2 : i < j 아님 → 멈춤한 번도 안 어긋남 → 회문이에요 (true)
i < j가 깨지면) 검사를 끝내요. 홀수 길이면 정중앙 글자(c)는 자기 자신과 비교할 필요가 없어서 그냥 지나쳐요. 어긋나는 순간 바로 false를 돌려주면 되니까, 최악이라도 절반만 훑어서 O(n)이에요.패턴 코드
조각을 모았다가 한 번에 합치기루프 안에서 += 하지 않는 것이 핵심이에요.
javascriptCopy codeconst parts = [];for (const c of s) {parts.push(transform(c));}return parts.join("");
알파벳을 인덱스로 쓰는 빈도수 배열
javascriptCopy codeconst count = new Array(26).fill(0);for (const c of s) {count[c.charCodeAt(0) - 97] += 1;}
양끝에서 좁혀오며 대칭 확인
javascriptCopy codelet i = 0;let j = s.length - 1;while (i < j) {if (s[i] !== s[j]) return false;i++;j--;}return true;
이번 주 문제
이번 주 진행0 / 4
셀프 체크
다음 주로 넘어가기 전에 소리 내어 답해 보세요. 막히면 그 부분이 아직 덜 익은 개념이에요.- 루프에서
s += c를 10만 번 하면 왜 느린지, 대안은 무엇인지 설명할 수 있는가? ord(c) - ord("a")가 왜 유용한가?- 문자열의 한 글자만 바꾸려면 어떤 절차를 거쳐야 하는가?