옹알이(2), 로또의 최고 순위와 최저 순위 , 대충 만든 자판, 체육복
코딩테스트 : 옹알이 2
문제 설명
머쓱이는 태어난 지 11개월 된 조카를 돌보고 있습니다. 조카는 아직 "aya", "ye", "woo", "ma" 네 가지 발음과 네 가지 발음을 조합해서 만들 수 있는 발음밖에 하지 못하고 연속해서 같은 발음을 하는 것을 어려워합니다. 문자열 배열 babbling이 매개변수로 주어질 때, 머쓱이의 조카가 발음할 수 있는 단어의 개수를 return하도록 solution 함수를 완성해주세요.
제한사항
1 ≤ babbling의 길이 ≤ 100
1 ≤ babbling[i]의 길이 ≤ 30
문자열은 알파벳 소문자로만 이루어져 있습니다.
using System;
public class Solution
{
public int solution(string[] babbling)
{
int answer = 0;
string[] repeated = new string[] { "11", "22", "33", "44" };
for (int i = 0; i < babbling.Length; i++)
{
babbling[i] = Replace(babbling[i]);
bool ok = true;
foreach (string b in repeated)
{
if (babbling[i].Contains(b)) ok = false;
}
babbling[i] = Replace2(babbling[i]);
if (babbling[i].Length >= 1) ok = false;
if (ok) answer++;
}
return answer;
}
public string Replace(string str)
{
str = str.Replace("aya", "1");
str = str.Replace("ye", "2");
str = str.Replace("woo", "3");
str = str.Replace("ma", "4");
return str;
}
public string Replace2(string str)
{
str = str.Replace("1", "");
str = str.Replace("2", "");
str = str.Replace("3", "");
str = str.Replace("4", "");
return str;
}
}
다른 사람의 흥미로운 풀이
나름 괜찮게 풀었다고 생각하고 있었는데 굳이 이럴 필요가 없었다.
우선 반복되는 객체들을 ex) ayaaya yeye 등을 먼저 숫자로 바꿔 주고, 한 번씩 반복되는 것을 지워준 다음 Length로 판단해도 되었을 것이다.
코딩테스트 : 로또의 최고 순위와 최저 순위
문제 설명
로또 6/45(이하 '로또'로 표기)는 1부터 45까지의 숫자 중 6개를 찍어서 맞히는 대표적인 복권입니다. 아래는 로또의 순위를 정하는 방식입니다. 1
순위 당첨 내용
1 6개 번호가 모두 일치
2 5개 번호가 일치
3 4개 번호가 일치
4 3개 번호가 일치
5 2개 번호가 일치
6(낙첨) 그 외
로또를 구매한 민우는 당첨 번호 발표일을 학수고대하고 있었습니다. 하지만, 민우의 동생이 로또에 낙서를 하여, 일부 번호를 알아볼 수 없게 되었습니다. 당첨 번호 발표 후, 민우는 자신이 구매했던 로또로 당첨이 가능했던 최고 순위와 최저 순위를 알아보고 싶어 졌습니다.
알아볼 수 없는 번호를 0으로 표기하기로 하고, 민우가 구매한 로또 번호 6개가 44, 1, 0, 0, 31 25라고 가정해보겠습니다. 당첨 번호 6개가 31, 10, 45, 1, 6, 19라면, 당첨 가능한 최고 순위와 최저 순위의 한 예는 아래와 같습니다.
당첨 번호 31 10 45 1 6 19 결과
최고 순위 번호 31 0→10 44 1 0→6 25 4개 번호 일치, 3등
최저 순위 번호 31 0→11 44 1 0→7 25 2개 번호 일치, 5등
순서와 상관없이, 구매한 로또에 당첨 번호와 일치하는 번호가 있으면 맞힌 걸로 인정됩니다.
알아볼 수 없는 두 개의 번호를 각각 10, 6이라고 가정하면 3등에 당첨될 수 있습니다.
3등을 만드는 다른 방법들도 존재합니다. 하지만, 2등 이상으로 만드는 것은 불가능합니다.
알아볼 수 없는 두 개의 번호를 각각 11, 7이라고 가정하면 5등에 당첨될 수 있습니다.
5등을 만드는 다른 방법들도 존재합니다. 하지만, 6등(낙첨)으로 만드는 것은 불가능합니다.
민우가 구매한 로또 번호를 담은 배열 lottos, 당첨 번호를 담은 배열 win_nums가 매개변수로 주어집니다. 이때, 당첨 가능한 최고 순위와 최저 순위를 차례대로 배열에 담아서 return 하도록 solution 함수를 완성해주세요.
제한사항
lottos는 길이 6인 정수 배열입니다.
lottos의 모든 원소는 0 이상 45 이하인 정수입니다.
0은 알아볼 수 없는 숫자를 의미합니다.
0을 제외한 다른 숫자들은 lottos에 2개 이상 담겨있지 않습니다.
lottos의 원소들은 정렬되어 있지 않을 수도 있습니다.
win_nums은 길이 6인 정수 배열입니다.
win_nums의 모든 원소는 1 이상 45 이하인 정수입니다.
win_nums에는 같은 숫자가 2개 이상 담겨있지 않습니다.
win_nums의 원소들은 정렬되어 있지 않을 수도 있습니다.
using System;
using System.Collections.Generic;
using System.Linq;
public class Solution {
public int[] solution(int[] lottos, int[] win_nums) {
int[] answer = new int[2];
List<int> winNumbers = win_nums.ToList();
int zeroCount = 0;
int correct = 7;
for (int i = 0; i < lottos.Length; i++)
{
if (winNumbers.Contains(lottos[i]))
{
winNumbers.Remove(lottos[i]);
correct--;
}
if (lottos[i] == 0) zeroCount++;
}
answer[0] = correct - zeroCount < 1 ? 1 : correct - zeroCount > 6 ? 6 : correct - zeroCount ;
answer[1] = correct >= 6 ? 6 : correct;
return answer;
}
}
마지막 asnwer[0] 코드가 복잡해진 이유는 내가 하나를 바꿔도 6등인 상황을 고려하지 않았기 때문이다.
예를 들어 1,2,3,4,5,6 이 있고, 6,7,8,9,10,11이 로또 번호라고 할 때
처음 했던 방식대로 correct - zeroCount로만 하면 여기서 7이 나와 버려 틀리게 된다.
코딩테스트 : 대충 만든 자판
문제 설명
휴대폰의 자판은 컴퓨터 키보드 자판과는 다르게 하나의 키에 여러 개의 문자가 할당될 수 있습니다. 키 하나에 여러 문자가 할당된 경우, 동일한 키를 연속해서 빠르게 누르면 할당된 순서대로 문자가 바뀝니다.
예를 들어, 1번 키에 "A", "B", "C" 순서대로 문자가 할당되어 있다면 1번 키를 한 번 누르면 "A", 두 번 누르면 "B", 세 번 누르면 "C"가 되는 식입니다.
같은 규칙을 적용해 아무렇게나 만든 휴대폰 자판이 있습니다. 이 휴대폰 자판은 키의 개수가 1개부터 최대 100개까지 있을 수 있으며, 특정 키를 눌렀을 때 입력되는 문자들도 무작위로 배열되어 있습니다. 또, 같은 문자가 자판 전체에 여러 번 할당된 경우도 있고, 키 하나에 같은 문자가 여러 번 할당된 경우도 있습니다. 심지어 아예 할당되지 않은 경우도 있습니다. 따라서 몇몇 문자열은 작성할 수 없을 수도 있습니다.
이 휴대폰 자판을 이용해 특정 문자열을 작성할 때, 키를 최소 몇 번 눌러야 그 문자열을 작성할 수 있는지 알아보고자 합니다.
1번 키부터 차례대로 할당된 문자들이 순서대로 담긴 문자열배열 keymap과 입력하려는 문자열들이 담긴 문자열 배열 targets가 주어질 때, 각 문자열을 작성하기 위해 키를 최소 몇 번씩 눌러야 하는지 순서대로 배열에 담아 return 하는 solution 함수를 완성해 주세요.
단, 목표 문자열을 작성할 수 없을 때는 -1을 저장합니다.
제한사항
1 ≤ keymap의 길이 ≤ 100
1 ≤ keymap의 원소의 길이 ≤ 100
keymap[i]는 i + 1번 키를 눌렀을 때 순서대로 바뀌는 문자를 의미합니다.
예를 들어 keymap[0] = "ABACD" 인 경우 1번 키를 한 번 누르면 A, 두 번 누르면 B, 세 번 누르면 A 가 됩니다.
keymap의 원소의 길이는 서로 다를 수 있습니다.
keymap의 원소는 알파벳 대문자로만 이루어져 있습니다.
1 ≤ targets의 길이 ≤ 100
1 ≤ targets의 원소의 길이 ≤ 100
targets의 원소는 알파벳 대문자로만 이루어져 있습니다.
using System;
using System.Collections.Generic;
public class Solution
{
public int[] solution(string[] keymap, string[] targets)
{
int[] answer = new int[targets.Length];
// 먼저 모든 키맵을 돌면서 A B C D E F G~ 를 체크해야함
string[] keys = new string[]
{
"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"
};
Dictionary<string, int> dict = new Dictionary<string, int>();
for (int i = 0; i < keymap.Length; i++)
{
for (int j = 0; j < keymap[i].Length; j++)
{
if( dict.ContainsKey(keymap[i][j].ToString()))
{
if (dict[keymap[i][j].ToString()] > j+1) dict[keymap[i][j].ToString()] = j+1;
}
else
{
dict.Add(keymap[i][j].ToString(), j+1);
}
}
}
for (int i = 0; i < targets.Length; i++)
{
int temp = 0;
for (int j = 0; j < targets[i].Length; j++)
{
string target = targets[i][j].ToString();
if (dict.ContainsKey(target)) temp += dict[target];
else
{
temp = -1;
break; }
}
answer[i] = temp;
}
return answer;
}
}
다른 사람의 흥미로운 풀이
풀긴 했지만 굳이 A부터 Z까지를 선언할 필요가 없었다. 어쩌피 없으면 -1을 리턴할 것이기 때문에 그냥 바로 Dic에 넣으면 된다.
코딩테스트 : 체육복
문제 설명
점심시간에 도둑이 들어, 일부 학생이 체육복을 도난당했습니다. 다행히 여벌 체육복이 있는 학생이 이들에게 체육복을 빌려주려 합니다. 학생들의 번호는 체격 순으로 매겨져 있어, 바로 앞번호의 학생이나 바로 뒷번호의 학생에게만 체육복을 빌려줄 수 있습니다. 예를 들어, 4번 학생은 3번 학생이나 5번 학생에게만 체육복을 빌려줄 수 있습니다. 체육복이 없으면 수업을 들을 수 없기 때문에 체육복을 적절히 빌려 최대한 많은 학생이 체육수업을 들어야 합니다.
전체 학생의 수 n, 체육복을 도난당한 학생들의 번호가 담긴 배열 lost, 여벌의 체육복을 가져온 학생들의 번호가 담긴 배열 reserve가 매개변수로 주어질 때, 체육수업을 들을 수 있는 학생의 최댓값을 return 하도록 solution 함수를 작성해주세요.
제한사항
전체 학생의 수는 2명 이상 30명 이하입니다.
체육복을 도난당한 학생의 수는 1명 이상 n명 이하이고 중복되는 번호는 없습니다.
여벌의 체육복을 가져온 학생의 수는 1명 이상 n명 이하이고 중복되는 번호는 없습니다.
여벌 체육복이 있는 학생만 다른 학생에게 체육복을 빌려줄 수 있습니다.
여벌 체육복을 가져온 학생이 체육복을 도난당했을 수 있습니다. 이때 이 학생은 체육복을 하나만 도난당했다고 가정하며, 남은 체육복이 하나이기에 다른 학생에게는 체육복을 빌려줄 수 없습니다.
using System;
using System.Collections.Generic;
using System.Linq;
public class Solution
{
public int solution(int n, int[] lost, int[] reserve)
{
int answer = 0;
List<int> lostNumbers = lost.ToList();
List<int> reserveNumbers = reserve.ToList();
for (int i = 0; i < lostNumbers.Count; i++)
{
if (reserveNumbers.Contains(lostNumbers[i]))
{
reserveNumbers.Remove(lostNumbers[i]);
lostNumbers.RemoveAt(i);
}
}
lostNumbers.Sort();
reserveNumbers.Sort();
for (int i = 0; i < lostNumbers.Count; i++)
{
//끝에서부터 검사
if (reserveNumbers.Contains(lostNumbers[i] - 1))
{
reserveNumbers.Remove(lostNumbers[i] - 1);
lostNumbers.RemoveAt(i);
}
if (reserveNumbers.Contains(lostNumbers[i] + 1))
{
reserveNumbers.Remove(lostNumbers[i] + 1);
lostNumbers.RemoveAt(i);
}
}
return n - lostNumbers.Count ;
}
}
한 1/3은 런타임 에러, 1/2은 실패가 떴다.
뭐가 잘못된건지 뚫어져라 쳐다보는 중이다.
우선 런타임 에러가 나는 이유를 먼저 찾아보고 그 다음 답이 틀린 이유를 찾아봐야지.
끝에서부터 나눠준다는 방식이 틀린건가 ? 한쪽 끝이 아니라 양 쪽 끝에서 접근해야 하나 ?
흠... 알아보니 리스트 순회 오류다.
리스트를 지워버리고 다음 인덱스로 넘어가 버려서 생긴 문제들이 있었다.
using System;
using System.Collections.Generic;
using System.Linq;
public class Solution
{
public int solution(int n, int[] lost, int[] reserve)
{
int answer = 0;
List<int> lostNumbers = lost.ToList();
List<int> reserveNumbers = reserve.ToList();
for (int i = 0; i < lostNumbers.Count; i++)
{
if (reserveNumbers.Contains(lostNumbers[i]))
{
reserveNumbers.Remove(lostNumbers[i]);
lostNumbers.RemoveAt(i);
i--;
}
}
lostNumbers.Sort();
reserveNumbers.Sort();
for (int i = 0; i < lostNumbers.Count; i++)
{
//끝에서부터 검사
if (reserveNumbers.Contains(lostNumbers[i] - 1))
{
reserveNumbers.Remove(lostNumbers[i] - 1);
lostNumbers.RemoveAt(i);
if (i > 0) i--;
}
if (reserveNumbers.Contains(lostNumbers[i] + 1))
{
reserveNumbers.Remove(lostNumbers[i] + 1);
lostNumbers.RemoveAt(i);
if (lostNumbers.Count != i) i--;
}
}
return n - lostNumbers.Count ;
}
}
이렇게 바꾸니 런타임 에러 2개, 실패 5개가 떴다. 분명 가까워지고 있다 !
using System;
using System.Collections.Generic;
using System.Linq;
public class Solution
{
public int solution(int n, int[] lost, int[] reserve)
{
int answer = 0;
List<int> lostNumbers = lost.ToList();
List<int> reserveNumbers = reserve.ToList();
for (int i = 0; i < lostNumbers.Count; i++)
{
if (reserveNumbers.Contains(lostNumbers[i]))
{
reserveNumbers.Remove(lostNumbers[i]);
lostNumbers.RemoveAt(i);
i--;
}
}
lostNumbers.Sort();
reserveNumbers.Sort();
for (int i = 0; i < lostNumbers.Count; i++)
{
//끝에서부터 검사
if (reserveNumbers.Contains(lostNumbers[i] - 1))
{
reserveNumbers.Remove(lostNumbers[i] - 1);
lostNumbers.RemoveAt(i);
i--;
continue;
}
if (reserveNumbers.Contains(lostNumbers[i] + 1))
{
reserveNumbers.Remove(lostNumbers[i] + 1);
lostNumbers.RemoveAt(i);
i--;
continue;
}
}
return n - lostNumbers.Count;
}
}
continue로 해결 !
결국 논리적 흐름이 완전하지 않아서 ... 분명 겪었던 문제다.
지금은 레벨 1을 풀면서 간단한 것들을 테스트 하고 있는 상황이니 다시 한 번 기억해두고 넘어가자.
List 등을 삭제할 때는 , 순회하지 않는 인덱스가 생길 수 있으므로 항상 조심해야 한다.