문제 설명
선분 3개가 평행하게 놓여 있습니다. 세 선분의 시작과 끝 좌표가 [[start, end], [start, end], [start, end]] 형태로 들어있는 2차원 배열 lines가 매개변수로 주어질 때, 두 개 이상의 선분이 겹치는 부분의 길이를 return 하도록 solution 함수를 완성해보세요. lines가 [[0, 2], [-3, -1], [-2, 1]]일 때 그림으로 나타내면 다음과 같습니다.
선분이 두 개 이상 겹친 곳은 [-2, -1], [0, 1]로 길이 2만큼 겹쳐있습니다.
제한사항
- lines의 길이 = 3
- lines의 원소의 길이 = 2
- 모든 선분은 길이가 1 이상입니다.
- lines의 원소는 [a, b] 형태이며, a, b는 각각 선분의 양 끝점 입니다.
- -100 ≤ a < b ≤ 100
풀이
이 문제도 접근방식을 모르고 처음 접한다면 꽤나 고전할 문제인거 같다.
처음엔 두 선분의 겹치는 길이를 구하는 함수를 만들어서 사용했었는데,
3개 이상 겹치는 구간에 대해서 다시 빼줘야 할때 계산된 겹치는 구간끼리 다시 계산을 하고..
이러다보니 코드가 너무 복잡해져서 이게 맞나.. 싶었다.
그러다가 생각을 좀 바꿔서 아래와 같이 접근해서 풀어냈다.
const solution = (lines) => {
const lineArr = lines.map(line => {
return Array.from(
{length: Math.abs(line[1] - line[0]) + 1},
(_, i) => i += line[0]
);
})
const lineObj = lineArr.reduce((acc, cur) => {
cur.forEach((el, idx) => {
if(idx === cur.length - 1) return;
acc[`${el}-${el+1}`] ? acc[`${el}-${el+1}`] += 1 : acc[`${el}-${el+1}`] = 1;
})
return acc;
}, {})
return Object.values(lineObj).filter(el => el > 1).length;
}
조금 더 간결하게 짤 수도 있을것 같긴한데.
이런경우 너무 고차함수들이 중첩되어 있으면 오히려 가독성이 안좋아 질 것 같아서
그냥 두기로 했다.
우선 주어진 3개의 선분을 시작점과 끝점까지 이루어진 배열인 lineArr로 만들어냈다.
즉, [0, 3] 이라는 선분이 주어진다면 [0,1,2,3] 이라는 배열이 반환되게 해놨다.
그다음, 그렇게 만들어진 3개의 배열을 가지고 reduce 매서드로 객체의 형태로
점 0을 지나가는 라인의 카운트를 체크하였다.
그러니까 [0, 3] 그리고 [1, 3] 이렇게 두 선분이 있다고 하면,
이걸 위의 lineArr로 변환하면
[[0, 1, 2, 3], [1, 2, 3]] 이런 배열이 될건데,
이걸 다시 reduce를 통해
{0: 1, 1: 2, 2: 3, 3: 2} 이렇게..
각각 0은 몇개 1은 몇개 2는 몇개 3은 몇개..
이런식으로 카운팅을 해주었다.
근데 이렇게 하면, 겹치지 않는 주제에 끝점이 닿아있어도 2로 카운팅이 되서 코드를 조금 바꾼게,
객체의 키 자체를 점의 인덱스로 하지 않고 `0-1` , `1-2`, `2-3` 등의 구간을 기준으로 하게 변경했다.
그리고 각 점에 카운트를 올리기 전에, 해당 선분의 마지막점인경우 체킹을 안하게 해주었다.
그러니까 [0,3]의 선분을 [0,1,2,3] 으로 변경하고 0부터
0-1 : +1
1-2 : +1
2-3 : +1
3은 해당 선분 배열의 마지막 원소이므로 넘어가기.
이렇게 하면 점이 아닌 구간으로 체크가 가능하고,
3개의 선분에 대해서 모든 구간을 체크 한 뒤,
최종적으로 나온 배열에서 숫자가 2 이상인 것들만 filter 매서드로 골라내서 length를 리턴했다.