🚀 기능 요구 사항

레벨 2의 팀 프로젝트 미션으로 SNS(Social Networking Service)를 만들고자 하는 팀이 있다. 팀에 속한 크루 중 평소 알고리즘에 관심이 많은 미스터코는 친구 추천 알고리즘을 구현하고자 아래와 같은 규칙을 세웠다.

  • 사용자와 함께 아는 친구의 수 = 10점
  • 사용자의 타임 라인에 방문한 횟수 = 1점

사용자 아이디 user와 친구 관계를 담은 이차원 배열 friends, 사용자 타임 라인 방문 기록 visitors가 매개변수로 주어질 때, 미스터코의 친구 추천 규칙에 따라 점수가 가장 높은 순으로 정렬하여 최대 5명을 return 하도록 solution 메서드를 완성하라. 이때 추천 점수가 0점인 경우 추천하지 않으며, 추천 점수가 같은 경우는 이름순으로 정렬한다.

제한사항

  • user는 길이가 1 이상 30 이하인 문자열이다.
  • friends는 길이가 1 이상 10,000 이하인 배열이다.
  • friends의 각 원소는 길이가 2인 배열로 [아이디 A, 아이디 B] 순으로 들어있다.
    • A와 B는 친구라는 의미이다.
    • 아이디는 길이가 1 이상 30 이하인 문자열이다.
  • visitors는 길이가 0 이상 10,000 이하인 배열이다.
  • 사용자 아이디는 알파벳 소문자로만 이루어져 있다.
  • 동일한 친구 관계가 중복해서 주어지지 않는다.
  • 추천할 친구가 없는 경우는 주어지지 않는다.

실행 결과 예시

user friends visitors result
“mrko” [ [“donut”, “andole”], [“donut”, “jun”], [“donut”, “mrko”], [“shakevan”, “andole”], [“shakevan”, “jun”], [“shakevan”, “mrko”] ] [“bedi”, “bedi”, “donut”, “bedi”, “shakevan”] [“andole”, “jun”, “bedi”]

내 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
function problem7(user, friends, visitors) {
if (
typeof user !== "string" ||
!Array.isArray(friends) ||
!Array.isArray(visitors)
)
throw new Error(
"user는 문자열, friends는 배열, visitors는 배열이여야 합니다."
);
if (!user.length || user.length > 30)
throw new RangeError("user는 길이가 1 이상 30 이하인 문자열이여야 합니다.");
if (!friends.length || friends.length > 10000)
throw new RangeError(
"friends는 길이가 1 이상 10,000 이하인 배열이여야 합니다."
);
if (visitors.length < 0 || visitors.length > 10000)
throw new RangeError(
"visitors는 길이가 0 이상 10,000 이하인 배열이여야 합니다."
);

// 유저의 친구들
let userFriends = [];
friends.forEach((friend) => {
if (friend.includes(user)) userFriends = [...userFriends, ...friend];
});
userFriends = userFriends.filter((friend) => friend !== user);

// 유저 친구의 친구들 => 알 수도 있는 친구들
let mayKnowUsers = [];
friends.forEach((friend) => {
userFriends.forEach((userFriend) => {
if (friend.includes(userFriend))
mayKnowUsers = [...mayKnowUsers, ...friend];
});
});

mayKnowUsers = mayKnowUsers.filter((mayKnowUser) => {
return mayKnowUser !== user && !userFriends.includes(mayKnowUser);
});

// 유저 점수 계산
const userCount = {};
mayKnowUsers.forEach((mayKnowUser) => {
userCount[mayKnowUser] = (userCount[mayKnowUser] ?? 0) + 10;
});

visitors.forEach((visitor) => {
if (visitor === user || userFriends.includes(visitor)) return;
userCount[visitor] = (userCount[visitor] ?? 0) + 1;
});

// 점수 기준 내림차순 정렬
const sortFunc = (userA, userB) => {
const numA = userCount[userA];
const numB = userCount[userB];

if (numA - numB > 0) return -1;
else if (numA - numB < 0) return 1;
else return userA < userB ? -1 : userA === userB ? 0 : 1;
};

const userId = Object.keys(userCount);

return userId.sort((userA, userB) => sortFunc(userA, userB)).slice(0, 5);
}
  • 유저의 친구들을 구하였다.
  • 유저의 친구들의 친구들을 구하였다. ⇒ 10점을 매기기 위해
  • 방문객과 유저의 친구의 친구들을 순회하면서 점수를 매겼다.
  • 점수를 기준으로 내림차순 정렬과 동일 점수일 때 알파벳 순으로 정렬을 해주었다.

🏓 소감

이 문제는 처음에 한국말을 이해하기 어려워서 오랫동안 고전했던 문제이다. 친구에게 문제를 차근히 설명해보면서 요구사항에 대해 파악할 수 있었다.

  • 시간이 부족하여 6번문제와 7번문제는 리팩터링을 많이 못한 것이 아쉬웠다.
  • 이전까지 sort() 메서드는 단순히 오름차순, 내림차순 정렬에만 사용되는 것이라고 생각했는데, 이번 기회에 sort() 메서드는 두개의 인자를 받아서 -1, 0, 1 값을 return 하는 것을 기준으로 배열의 index를 직접 변경하는 메서드이라는 것을 제대로 알게되었다.

댓글 공유

🚀 기능 요구 사항

우아한테크코스에서는 교육생(이하 크루) 간 소통 시 닉네임을 사용한다. 간혹 비슷한 닉네임을 정하는 경우가 있는데, 이러할 경우 소통할 때 혼란을 불러일으킬 수 있다.

혼란을 막기 위해 크루들의 닉네임 중 같은 글자가 연속적으로 포함 될 경우 해당 닉네임 사용을 제한하려 한다. 이를 위해 같은 글자가 연속적으로 포함되는 닉네임을 신청한 크루들에게 알려주는 시스템을 만들려고 한다.

신청받은 닉네임 중 같은 글자가 연속적으로 포함 되는 닉네임을 작성한 지원자의 이메일 목록을 return 하도록 solution 메서드를 완성하라.

제한사항

  • 두 글자 이상의 문자가 연속적으로 순서에 맞추어 포함되어 있는 경우 중복으로 간주한다.
  • 크루는 1명 이상 10,000명 이하이다.
  • 이메일은 이메일 형식에 부합하며, 전체 길이는 11자 이상 20자 미만이다.
  • 신청할 수 있는 이메일은 email.com 도메인으로만 제한한다.
  • 닉네임은 한글만 가능하고 전체 길이는 1자 이상 20자 미만이다.
  • result는 이메일에 해당하는 부분의 문자열을 오름차순으로 정렬하고 중복은 제거한다.

실행 결과 예시

forms result
[ [“jm@email.com“, “제이엠”], [“jason@email.com“, “제이슨”], [“woniee@email.com“, “워니”], [“mj@email.com“, “엠제이”], [“nowm@email.com“, “이제엠”] ] [“jason@email.com“, “jm@email.com“, “mj@email.com“]

내 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
const getPartialStr = (str) => {
const res = [];
let limit = 2;
while (limit <= str.length) {
const output = [];
let index = 0;
while (true) {
const seperated = [...str].slice(index, index + limit).join("");
index += 1;
if (limit > seperated.length) break;
output.push(seperated);
}
limit += 1;
res.push(...output);
}

return res;
};

const getDuplicatedUser = (arr) => {
let index = 0;
let position = 1;
const res = [];
while (index !== arr.length - 1) {
const set = new Set([...arr[index], ...arr[position]]);
if (set.size !== arr[index].length + arr[position].length) {
res.push(...arr[index].slice(-1), ...arr[position].slice(-1));
}
position += 1;
if (position === arr.length) {
index += 1;
position = index + 1;
}
}
return [...new Set(res)];
};

function problem6(forms) {
if (!forms.length || forms.length > 10000)
throw new RangeError("크루는 1명 이상 10,000명 이하이여야 합니다.");

const users = {};
forms.forEach((form) => {
users[form[1]] = form[0];
});
const nicknames = forms.map((nickname) => nickname[1]);
const partialStrs = nicknames.map((nickname) => getPartialStr(nickname));
const selectedUsers = getDuplicatedUser(partialStrs);

return selectedUsers.map((selectedUser) => users[selectedUser]).sort();
}
  • 닉네임의 첫번째 글자부터 2글자 이상으로 만들 수 있는 모든 부분 문자열을 구하였다.
  • 각 닉네임의 부분 문자열 배열을 비교하여 중복요소가 하나라도 있으면 중복 문자 포함 유저 배열에 등록하였다.
  • 해당 유저의 이메일을 오름차순으로 정렬한 배열을 반환하였다.

🏓 소감

우선 이 문제를 풀면서 가장 큰 성장을 이뤄냈다고 자축하고 싶었다. 왜냐하면 이와 비슷한 부분 문자열 문제를 못풀어서 스트레스를 받았던 적이 있는데 이번 기회에 나름의 방법(?)을 찾아서 구현에 성공하여 기분이 좋았다.

  • 2글자 이상으로 만들 수 있는 부분 문자열을 모두 구했기 때문에 코드가 매우 복잡해졌다. 하지만 2글자 부분 문자열만 구하여 서로 비교하여도 충분히 문제가 없을 상황이기 때문에 이 부분이 좀 아쉬웠다.
  • while 문을 사용할 경우와 for 문을 사용할 경우가 애매했었는데, 이번에 코드리뷰를 진행하면서 while 문은 특정 조건이 변경되는 상황이라면 사용하도록 하고 for 문은 일정 횟수를 반복해야 하는 상황에서 사용하도록 한다는 것을 알게 되어 상황에 맞게 적절히 사용할 수 있을 것 같다.

댓글 공유

🚀 기능 요구 사항

계좌에 들어있는 돈 일부를 은행에서 출금하고자 한다. 돈 담을 지갑이 최대한 가볍도록 큰 금액의 화폐 위주로 받는다.

돈의 액수 money가 매개변수로 주어질 때, 오만 원권, 만 원권, 오천 원권, 천 원권, 오백원 동전, 백원 동전, 오십원 동전, 십원 동전, 일원 동전 각 몇 개로 변환되는지 금액이 큰 순서대로 배열에 담아 return 하도록 solution 메서드를 완성하라.

제한사항

  • money는 1 이상 1,000,000 이하인 자연수이다.

실행 결과 예시

money result
50237 [1, 0, 0, 0, 0, 2, 0, 3, 7]
15000 [0, 1, 1, 0, 0, 0, 0, 0, 0]

내 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function problem5(money) {
if (!money || money > 1000000)
throw new RangeError(
"매개변수는 1 이상 1,000,000 이하의 자연수여야 합니다."
);

const papers = [50000, 10000, 5000, 1000, 500, 100, 50, 10, 1];
let paperCount = [];

papers.forEach((paper) => {
paperCount = [...paperCount, Math.floor(money / paper)];
money %= paper;
});

return paperCount;
}
  • 나머지와 몫을 이용해 반복문을 통해 구현하였다.

🏓 소감

  • map 메서드는 배열의 요소에 콜백함수를 호출하여 그 반환값으로 새로운 배열을 만드는 메서드라고만 생각하고 처음에 papers 배열을 map 메서드를 사용하여 map 메서드 내부에서 money를 변형시켜 비순수한 함수로 구현하였다. 하지만 함수는 외부 변수를 변경하지 않고 순수하도록 작성해야하므로 이를 forEach 메서드로 변경하였다.
  • 여기서는 parseInt 대신에 Math.floor() 메서드를 사용하여 정수로 변형시켜주었다.

댓글 공유

🚀 기능 요구 사항

어느 연못에 엄마 말씀을 좀처럼 듣지 않는 청개구리가 살고 있었다. 청개구리는 엄마가 하는 말은 무엇이든 반대로 말하였다.

엄마 말씀 word가 매개변수로 주어질 때, 아래 청개구리 사전을 참고해 반대로 변환하여 return 하도록 solution 메서드를 완성하라.

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Z Y X W V U T S R Q P O N M L K J I H G F E D C B A

제한사항

  • word는 길이가 1 이상 1,000 이하인 문자열이다.
  • 알파벳 외의 문자는 변환하지 않는다.
  • 알파벳 대문자는 알파벳 대문자로, 알파벳 소문자는 알파벳 소문자로 변환한다.

실행 결과 예시

word result
“I love you” “R olev blf”

내 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function problem4(word) {
if (typeof word !== 'string') throw new TypeError('매개변수는 문자열 타입이여야 합니다.');
if (!word.length || word.length > 1000) throw new RangeError('매개변수는 1 이상 1,000 이하의 문자열이여야 합니다.');

const ALPHABET_LENGTH = 26;
const ASCII_NUMBER_CODE_UPPER_A = 65;
const ASCII_NUMBER_CODE_UPPER__Z = 90 ;
const ASCII_NUMBER_CODE_LOWER_A = 97;
const ASCII_NUMBER_CODE_LOWER__Z = 122;

const splitWords = [...word];
const uppers = Array.from({length:ALPHABET_LENGTH}, (_,i) => String.fromCharCode(ASCII_NUMBER_CODE_UPPER_A+i));
const lowers = Array.from({length:ALPHABET_LENGTH}, (_,i) => String.fromCharCode(ASCII_NUMBER_CODE_LOWER_A+i));

const changeOppositeStr = str => {
const codePosition = str.charCodeAt();

return (codePosition >= ASCII_NUMBER_CODE_UPPER_A && codePosition <= ASCII_NUMBER_CODE_UPPER__Z
? [...uppers].reverse()[uppers.indexOf(str)]
: codePosition >= ASCII_NUMBER_CODE_LOWER_A && codePosition <= ASCII_NUMBER_CODE_LOWER__Z
? [...lowers].reverse()[lowers.indexOf(str)]
: str);
}

return splitWords.map(splitedWord => changeOppositeStr(splitedWord)).join('');
}
  • ASCII 코드를 활용하여 AZ, az 를 담은 배열을 생성하였다.
  • map 메서드를 사용하여 각 요소를 반대편 문자열로 바꿔주었다.

🏓 소감

  • 리팩터링을 하기 전 코드는 ASCII 코드를 상수에 할당하지 않고 그대로 비즈니스 로직에 가져다가 사용하지 해당 숫자가 무엇을 의미하는지 파악하기 힘들어 상수에 할당하니 가독성이 높아졌다.
  • changeOppositeStr() 함수 내부에서 reverse()[] 이 부분이 중복되어 함수로 추출하려 했지만, 짧은 코드에 너무 많은 함수가 오히려 더 가독성을 해친다고 판단하여 그대로 두었다.
  • 로직을 ASCII 코드 대신 정규표현식으로 구현하였다면 비즈니스 로직이 훨씬 더 간결해질 것 같기에 다음에 문자열 문제가 나온다면 정규표현식으로도 도전해봐야겠다.

댓글 공유

🚀 기능 요구 사항

배달이가 좋아하는 369게임을 하고자 한다. 놀이법은 1부터 숫자를 하나씩 대면서, 3, 6, 9가 들어가는 숫자는 숫자를 말하는 대신 3, 6, 9의 개수만큼 손뼉을 쳐야 한다.

숫자 number가 매개변수로 주어질 때, 1부터 number까지 손뼉을 몇 번 쳐야 하는지 횟수를 return 하도록 solution 메서드를 완성하라.

제한사항

  • number는 1 이상 10,000 이하인 자연수이다.

실행 결과 예시

number result
13 4
33 14

내 코드

1
2
3
4
5
6
7
8
9
10
11
12
function problem3(number) {
if (number <= 0 || number > 10000 || number !== parseInt(number)) throw new Error('매개변수는 1이상 10,000 이하의 자연수만 입력 가능합니다.');

let clapCount = 0;
for (let i = 1; i < number + 1; i++) {
const splitStr = [...String(i)];

splitStr.forEach(elem => clapCount = elem === '3' || elem === '6' || elem === '9' ? clapCount + 1 : clapCount);
}

return clapCount;
}
  • 매개변수만큼 반복문을 돌면서 숫자 1부터 문자열로 변환한 다음 문자열에 3,6,9가 있으면 clapCount를 1 증가시켜주는 코드를 작성하였다.

🏓 소감

  • parseInt() 메서드 대신 +number를 사용하여 형변환을 시켰으면 어떨까하는 아쉬움이 남는다. 왜냐하면 parseInt() 메서드는 특정 진수법에 해당하는 정수로 반환하는 역할을 하기에 단순히 숫자로 형변환을 위함이 목적이라면 의미가 벗어난다고 생각했다. 마찬가지로 String() 메서드 대신 (i + ‘’)로 형변환 시켜야겠다.
  • 수강생들과 코드리뷰를 하면서 느낀점은 문자열을 다룰 때, 정규표현식을 사용할 수 있다면 좀 더 가독성이 좋은 코드를 작성할 수 있을 것 이라는 생각이 들었다. 지금부터라도 문자열 문제가 나온다면 정규표현식으로 풀어보는 연습을 해야겠다.

댓글 공유

boj-1157 단어공부

카테고리 Algorithm, boj

📌 문제

알파벳 대소문자로 된 단어가 주어지면, 이 단어에서 가장 많이 사용된 알파벳이 무엇인지 알아내는 프로그램을 작성하시오. 단, 대문자와 소문자를 구분하지 않는다.

입력

첫째 줄에 알파벳 대소문자로 이루어진 단어가 주어진다. 주어지는 단어의 길이는 1,000,000을 넘지 않는다.

출력

첫째 줄에 이 단어에서 가장 많이 사용된 알파벳을 대문자로 출력한다. 단, 가장 많이 사용된 알파벳이 여러 개 존재하는 경우에는 ?를 출력한다.

입력 출력
Mississipi ?
zZa Z
z Z
baaa A

내 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const input = require("fs").readFileSync("/dev/stdin").toString().trim();

const alphabets = [...new Set([...input.toLowerCase()])];
let max = 0;
let current = ''

alphabets.forEach((alphabet,i) => {
const regExp = new RegExp(alphabet, 'ig');
const count = input.match(regExp).length;
if (max < count) {
max = count;
current = alphabet.toUpperCase();
}
else if (max === count && alphabet.toUpperCase() !== current) current = '?';
})
console.log(current)
  1. 입력값의 알파벳을 소문자로 만들고 중복을 제거해주었다.
  2. 해당 알파벳을 forEach 문으로 순회하면서 초기에 입력값으로 주어진 문자열을 정규표현식으로 확인한다.
  3. 정규표현식에 매치되는 요소의 갯수가 최대값으로 갱신해주고 그 때의 알파벳을 대문자로 저장해둔다.
  4. 만약 새로운 알파벳의 갯수가 최댓값과 같고 현재의 알파벳 대문자와 current에 할당된 알파벳이 같으면 current에 “?”를 할당한다.

🏓 소감

문자열을 풀 때, 정규표현식을 사용하면 반복문을 줄이고 코드를 깔끔하게 하여 가독성을 키우는 연습을 하는데 도움이 될 것 같다.

주어진 테스트 케이스를 다 만족했지만, 제출했을 때 오답이라고 나와서 당황했다. 어떻게 하면 내 제출이 만족 못시키는 케이스가 있을까하고 생각해보면서 여러가지 상황을 제시하면서 테스트 케이스를 찾아내었다.

그 결과 처음 정규표현식을 생성할 때, alphabets 배열을 순회하면서가 아닌 초기 입력값의 순회한 요소를 넣어주었기 때문에 제대로 된 결과가 나오지 않는다는 것을 깨닫고 수정하였다.

테스트 케이스를 어떻게 찾아내지라고 생각하면서 막막했었는데,

  1. 주어진 문제에서 제한사항 중 내가 놓친 부분이 있는지를 확인한다.
  2. 조건을 만족시키는 다양한 상황을 만들어본다.

위 두가지를 실행해보다보니 찾게되어서 기뻤다.

댓글 공유

🚀 기능 요구 사항

암호문을 좋아하는 괴짜 개발자 브라운이 이번에는 중복 문자를 이용한 새로운 암호를 만들었다. 예를 들어 “browoanoommnaon”이라는 암호문은 다음과 같은 순서로 해독할 수 있다.

  1. “browoanoommnaon”
  2. “browoannaon”
  3. “browoaaon”
  4. “browoon”
  5. “brown”

임의의 문자열 cryptogram이 매개변수로 주어질 때, 연속하는 중복 문자들을 삭제한 결과를 return 하도록 solution 메서드를 완성하라.

제한사항

  • cryptogram은 길이가 1 이상 1000 이하인 문자열이다.
  • cryptogram은 알파벳 소문자로만 이루어져 있다.

실행 결과 예시

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function problem2(cryptogram) {
if (cryptogram !== cryptogram.toLowerCase()) throw new TypeError('매개변수는 소문자만 입력할 수 있습니다.')
if (!cryptogram.length || cryptogram.length > 1000) throw new RangeError('매개변수는 1자 이상 1000자 이하만 입력할 수 있습니다.')

let strArr = [...cryptogram];
let duplicatedStrPosition = [];
let hasDuplicatedStr = true;

while(hasDuplicatedStr) {
strArr.forEach((_, i, init) => {
if (init[i] === init[i+1]) duplicatedStrPosition = [i, ...duplicatedStrPosition];
})

hasDuplicatedStr = !duplicatedStrPosition.length ? false : true;

duplicatedStrPosition = duplicatedStrPosition.filter(elem => {
strArr = [...strArr.slice(0, elem), ...strArr.slice(elem + 2)];
return false;
})
}

return strArr.join('');
}
  • strArr로 문자열을 배열로 관리
  • strArr 배열이 더 이상 중복이 없을 때 까지 반복해야하고, 문자열의 순회하며 중복을 확인해야 하므로 시간 복잡도 $n^2$
  • Spread문법으로 원본 배열의 변형을 최소화하는 방향으로 배열을 관리
  • for문 대신 배열 메서드 forEach문 사용
  • 원본 배열을 변형시키는 splice 메서드 대신 slice 메서드 사용

🏓 소감

  • 내가 코드를 처음 보는 사람의 입장으로 변수명부터 잘 이해할 수 있는지 고려하여 리팩터링을 하였다.
  • 처음에는 while 조건식을 단순히 true라고 하였는데, 의미를 부여하여 코드의 가독성을 높이기 위해 변수에 할당해주었다.
  • 배열을 다루기 때문에 for문 대신 forEach문을 사용하였다.
  • splice 메서드 같이 원본을 변형시키는 메서드 대신 spread 문법과 slice 메서드를 사용하였다.
  • 리팩터링 전에는 duplicatedStrPosition 배열을 직접 빈 배열로 초기화해주었지만, 리팩터링을 하면서 filter 메서드를 사용하여 좀 더 배열을 다루는 의미를 부여하였다.

하나 걸리는 부분은 제한사항을 에러처리를 해주어야 하나 고민을 하였다. 사용자가 제한사항에 벗어나는 입력을 할 수도 있으므로 이에 대한 에러를 발생시켜주는 것이 사용성에 더 옳다고 생각하여 if 조건문을 통해 에러처리를 해주었습니다.

에러처리에 대해서는 깊게 생각해보지 못했는데, 제한사항에 대해 한번 더 깊이 생각해보면서 에러처리의 필요성을 느낄 수 있었던 문제여서 즐거웠다.

또한 stack 자료구조를 생각하면서 문제를 해석하면 지금의 코드보다 훨씬 더 간결하고 가독성있는 코드를 구현할 수 있다는 것을 오늘 코드리뷰를 통해 깨닫게 되었다. stack으로 구현해보는 것은 다음 시간에 해보도록 하자.

댓글 공유

📌 문제

포비와 크롱이 페이지 번호가 1부터 시작되는 400 페이지의 책을 주웠다. 책을 살펴보니 왼쪽 페이지는 홀수, 오른쪽 페이지는 짝수 번호이고 모든 페이지에는 번호가 적혀있었다. 책이 마음에 든 포비와 크롱은 페이지 번호 게임을 통해 게임에서 이긴 사람이 책을 갖기로 한다. 페이지 번호 게임의 규칙은 아래와 같다.

  1. 책을 임의로 펼친다.
  2. 왼쪽 페이지 번호의 각 자리 숫자를 모두 더하거나, 모두 곱해 가장 큰 수를 구한다.
  3. 오른쪽 페이지 번호의 각 자리 숫자를 모두 더하거나, 모두 곱해 가장 큰 수를 구한다.
  4. 2~3 과정에서 가장 큰 수를 본인의 점수로 한다.
  5. 점수를 비교해 가장 높은 사람이 게임의 승자가 된다.
  6. 시작 면이나 마지막 면이 나오도록 책을 펼치지 않는다.
  7. 포비와 크롱이 펼친 페이지가 들어있는 배열 pobi와 crong이 주어질 때, 포비가 이긴다면 1, 크롱이 이긴다면 2, 무승부는 0, 예외사항은 -1로 return 하도록 solution 메서드를 완성하라.

제한사항

  • pobi와 crong의 길이는 2이다.
  • pobi와 crong에는 [왼쪽 페이지 번호, 오른쪽 페이지 번호]가 순서대로 들어있다.

실행 결과 예시

pobi crong result
[97, 98] [197, 198] 0
[131, 132] [211, 212] 1
[99, 102] [211, 212] -1

나의 해설

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function problem1(pobi, crong) {
const [pobiLeft, pobiRight] = pobi;
const [crongLeft, crongRight] = crong;

if (pobiRight - pobiLeft !== 1 || crongRight - crongLeft !== 1) return -1;

const sum = (str) =>
str
.split("")
.map((v) => +v)
.reduce((acc, cur) => acc + cur, 0);
const multiply = (str) =>
str
.split("")
.map((v) => +v)
.reduce((acc, cur) => acc * cur, 1);

const getMax = (left, right) => {
const leftMax = Math.max(sum(String(left)), multiply(String(left)));
const rightMax = Math.max(sum(String(right)), multiply(String(right)));
return Math.max(leftMax, rightMax);
};

const pobiMax = getMax(pobiLeft, pobiRight);
const crongMax = getMax(crongLeft, crongRight);

return pobiMax > crongMax ? 1 : pobiMax < crongMax ? 2 : 0;
}
  1. 우선 자주 사용될 것 같은 left 페이지 수와 right 페이지 수를 배열 구조분해 할당으로 변수에 할당해주었다.

  2. 이후 예외가 등장했을 때, 빠르게 결과를 내기 위해 바로 예외 처리를 해주었다.

  3. 각 페이지의 자릿수의 합과 곱을 구하는 함수를 생성해주었다.

  4. left와 right의 결과값 중 높은 값을 구하는 함수를 생성하였다.

  5. pobi의 최댓값과 crong의 최댓값을 비교하여 결과를 반환하였다.

🏓 소감

우아한 테크코스 프리코스 첫 문제를 받아보고 내가 이 문제를 푼 방법을 나열하며 정리해보았다. 논리의 흐름을 정리하니 다음번에 비슷한 문제가 나오면 더 빠른 시간 내에 풀고 다양한 시도를 해볼 수 있을 것 같다.

앞으로 4주간 프리코스가 진행될텐데 합격하든 불합격하든 나의 성장을 위해 모든 문제에 대한 풀이를 작성해보도록 할 것이다.

댓글 공유

type 체크 문제 Tip

카테고리 Algorithm

문제

입력값에 {}, [], null이 들어가면 출력값으로 type을 반환하도록 하는 함수를 만들어라

내 코드

1
2
3
4
5
6
7
8
9
10
11
const typeIs = (data) => {
if (typeof data !== "object") {
throw new TypeError(`${data}는 'object' 타입이 아닙니다.`);
}
if (Array.isArray(data)) {
return "array";
} else if (data === null) {
return "null";
}
return "object";
};
  • 입력값에 object 타입만 받을 수 있도록 에러처리를 해주었다.
  • if, else if 문으로 조건에 따라 반환값을 반환해주었다.

새로운 코드

1
2
3
4
const typeIs = data => {
return ({}).toString.call(data).toLowerCase().slice(8, -1);
}
toString() 메서드는 “[object type]”을 반환한다.
  • call 메서드를 사용하여 data 값을 바인딩 toString() 메서드에 바인딩 해주었다.

댓글 공유

boj-2525 오븐시계

카테고리 Algorithm, boj

boj 2525 오븐시계

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const fs = require("fs");
const input = fs.readFileSync("dev/stdin").toString().trim().split("\n");

let time = input[0].split(" ");
let oven = +input[1];

let hour = +time[0];
let minute = +time[1] + oven;

if (minute >= 60) {
hour += Math.floor(minute / 60);
minute %= 60;
hour = hour >= 24 ? hour - 24 : hour;
}

console.log(hour + " " + minute);

맞는데 왜 틀리지 를 10번 넘게 한 문제이다.

핵심은 oven 시간이 148분인 경우이다. 이 경우 시간이 2시간이 추가 되어야 하고 148 을 60으로 나눈 나머지를 minute에다가 더해주면 된다.

나는 1시간만 추가된 경우만 고려해줘서 틀렸다.

댓글 공유

loco9939

author.bio


author.job