[Jacoste] 알고리즘 공부

tamagoyakii·2023년 1월 1일
0

Jacoste

목록 보기
3/9
post-thumbnail

1. 구분자를 포함시키는 정규식

<수식 최대화> 문제는 인자로 들어오는 "100-200*300-500+20" 형식의 문자열을 3가지의 연산 문자 (+, -, *)의 우선순위를 다르게 하여 계산한 결과 중 가장 큰 값을 반환시키는 문제다.

이 문제를 풀기 위해 먼저 문자열을 split() 메서드로 나눠야겠다는 생각을 했다.

const a = '100-200*300-500+20';
const b = a.split('');

console.log(b);
// ['1', '0', '0', '-', '2', '0', '0', '*', '3', '0', '0', '-', '5', '0', '0', '+', '2', '0']

확실히 위의 방법으로 나누어선 안되겠다. 숫자와 연산자를 분리해서 나누고 싶었는데, 반복문이나 reduce() 메서드를 사용하지 않고 split() 메서드만을 사용하여 나누는 방법이 있을 것만 같았다. 검색을 해보니 구분자를 포함하려면 정규식에 괄호()를 넣어야한다고 했다. 그렇게 만들어진 식이다.

const a = '100-200*300-500+20';
const b = a.split(/(\*|\+|\-)/);

console.log(b);
// ['100', '-', '200', '*', '300', '-', '500', '+', '20'];

사실 이 친구를 아래와 같이 쓸 수도 있다.

const a = '100-200*300-500+20';
const b = a.split(/\b/);

console.log(b);
// ['100', '-', '200', '*', '300', '-', '500', '+', '20'];

정규 표현식 \b는 이전 포스팅에서도 다뤘었다. word boundary, 즉 단어의 경계에 대응하는 정규 표현식이다. non-word character ([^_a-zA-Z0-9])인 요소들을 가리킨다. 사실 위와 같이 쓰면 연산자들이 제거된 상태로 할당될 줄 알고 a.split(/(\b)/) 의 형식으로 써야 하는 게 아닌가 생각했는데, 아니었다!!!! \b는 어떤 지점을 의미하는 것이지, 특정 문자와 매칭되는 것이 아니기 때문이다. 충 격 !

2. array.splice()

splice의 인자들에 대한 이해가 부족했다!

const cal = {
    '+': (a, b) => a + b,
    '*': (a, b) => a * b,
    '-': (a, b) => a - b
}
    
const search = (arr, o) => {
    for (let i = arr.indexOf(o); i !== -1; i = arr.indexOf(o)) {
        arr[i - 1] = cal[o](arr[i - 1], arr[i + 1]);
        arr.splice(i, 2);
    }
    return arr;
}

위의 search() 함수에는 [200, '+', 300, '-', 10, '*', 200, '-', 30]의 형태의 인자 arr'+', '-', '*' 형태의 인자 o가 들어온다. 반복문을 돌면서 만약 arro가 포함되어 있는 경우 해당 인덱스(i) 앞(i - 1), 뒤(i + 1)를 cal() 함수로 계산한 뒤 앞의 인덱스(i - 1)에 재할당하고 뒤의 두개(i, i + 1)를 splice() 메서드로 제거하는 식이다. 이는 내가 splice() 메서드를 제대로 이해하지 못하고 있다는!!!!!!! 뜻이었다!!!!!!

array.splice(start[, deleteCount[, item1[, item2[, ...]]]]);

splice() 함수는 3개의 인자를 받을 수 있다. 지금까지 2개만 사용해왔기 때문에 마지막 인자를 사용해 본 적이 없는 것 같다.

  1. start : 배열의 변경을 시작할 인덱스
  2. deleteCount : 배열에서 제거할 요소의 수
  3. item : 배열에 추가할 요소

세 번째 인자를 활용하면 위의 식은 다음과 같이 바뀔 수 있다!

const search = (arr, o) => {
    for (let i = arr.indexOf(o); i !== -1; i = arr.indexOf(o)) {
        arr.splice(i - 1, 3, cal[o](arr[i - 1], arr[i + 1]));
    }
    return arr;
}

3. string.replaceAll()

<방금그곡>의 인자에는 [C, C#, D, D#, E, F, F#, G, G#, A, A#, B]으로 구성된 악보들이 주어진다. 악보가 문자열로 주어지기 때문에 #이 붙은 음들이 불편해지기 시작한다. 때문에 #이 붙은 녀석들을 다른 문자로 치환하기로 했다.

const replace = (o) => {
    const n = {'C#': 'H', 'D#': 'I', 'F#': 'J', 'G#': 'K', 'A#': 'L'};
    Object.keys(n).forEach((el) => {
        if (o.includes(el)) o = o.split(el).join(n[el])
    });
    return o;
}

나는 반복문을 통해 n의 키로 split()하고 해당 키의 값으로 join()하는 방법을 사용했다. 하지만 replaceAll() 이라는 메서드를 사용하면 그럴 필요가 없다!

const replace = (o) => {
    const n = {'C#': 'H', 'D#': 'I', 'F#': 'J', 'G#': 'K', 'A#': 'L'};
    Object.keys(n).forEach((el) => o = o.replaceAll(el, n[el]));
    return o;
}

replaceAll() 메서드는 replace() 메서드와 같은 방식으로 작동한다. 다른 점은 replace() 메서드는 일치하는 문자열이 여러 개인 경우 가장 먼저 찾은(앞에 있는) 문자열만 치환해 주지만, replaceAll() 메서드는 일치하는 모든 문자열을 치환해 준다. 두 메서드 모두 원본을 변형시키지 않고 새로운 문자열을 반환한다.

0개의 댓글