한자리 숫자가 적힌 종이 조각이 흩어져있습니다.
흩어진 종이 조각을 붙여 소수를 몇 개 만들 수 있는지 알아내려 합니다.
각 종이 조각에 적힌 숫자가 적힌 문자열 numbers가 주어졌을 때,
종이 조각으로 만들 수 있는 소수가 몇 개인지 return 하도록 solution 함수를 완성해주세요.
public class PrimeNumberGenerator
{
private static List<int> _numbersList = new List<int>();
private static List<int> _primeNumberList = new List<int>();
public static int Solution(string numbers)
{
InitNumbersList(numbers);
int maxNum = _numbersList.Max();
InitPrimeNumberList(maxNum);
return _numbersList.Intersect(_primeNumberList).Count();
}
private static void InitNumbersList(string numbers)
{
//순열을 구성할 숫자 전체 세트를 구성
int[] numberSet = new int [numbers.Length];
for (int i = 0; i < numbers.Length; i++)
{
numberSet[i] = int.Parse(numbers[i].ToString());
}
//이전에 조합에 사용된 숫자를 저장
List<int> prevDigitNumbers = numberSet.ToList();
//전체 자리수에 해당하는 숫자까지 전부 생성하도록 반복
for (int i = 0; i < numbers.Length; i++)
{
//이번에 새로 조합될 숫자를 저장
List<int> currentDigitNumbers = new List<int>();
//이전에 조합된 숫자를 사용
foreach (int number in prevDigitNumbers)
{
//새로운 숫자를 이어붙일 때 현재 이미 사용 중인 숫자를 포함하지 않도록 함
string numStr = number.ToString();
List<int> usingNumbers = numberSet.ToList();
//기존 숫자에서 사용하던 숫자를 제외하고 추가
foreach (char c in numStr)
{
usingNumbers.Remove(int.Parse(c.ToString()));
}
//사용하기로 결정된 숫자만 기존 숫자 뒤에 이어붙힘
foreach (int n in usingNumbers)
{
int tempNum = int.Parse(numStr + n);
//중복되는 숫자는 포함하지 않음
if(!currentDigitNumbers.Contains(tempNum))
currentDigitNumbers.Add(tempNum);
}
}
_numbersList.AddRange(prevDigitNumbers);
prevDigitNumbers = currentDigitNumbers;
}
_numbersList.AddRange(prevDigitNumbers);
}
private static void InitPrimeNumberList(int maxNum)
{
for (var i = 2; i <= maxNum; i++)
{
if(IsPrime(i))
_primeNumberList.Add(i);
}
}
private static bool IsPrime(int number)
{
for (int i = 2; i <= Math.Sqrt(number); i++)
{
if (number % i == 0) return false;
}
return true;
}
}
문제 풀이 단계
- 가능한 순열 조합 모두 찾기
- 해당 조합에서 소수 찾기
- 개수를 반환
예시)
12345
가 주어진다면
1자리 숫자 :1
,2
,3
,4
,5
...
7자리 숫자 :12345
,13245
... 등의 숫자가 존재해당 과정에서 2자리 숫자로 갈 때
2자리 숫자 :12
,13
,14
,15
,21
,23
...
기존 1자리 숫자에서 해당 숫자에 해당하지 않는 요소 중에 하나를 뒤에 붙이는 과정으로 순열을 구성할 수 있다는 것을 알게됨
private static List<int> _numbersList = new List<int>();
private static void InitNumbersList(string numbers)
{
//순열을 구성할 숫자 전체 세트를 구성
int[] numberSet = new int [numbers.Length];
for (int i = 0; i < numbers.Length; i++)
{
numberSet[i] = int.Parse(numbers[i].ToString());
}
//이전에 조합에 사용된 숫자를 저장
List<int> prevDigitNumbers = numberSet.ToList();
//전체 자리수에 해당하는 숫자까지 전부 생성하도록 반복
for (int i = 0; i < numbers.Length; i++)
{
//이번에 새로 조합될 숫자를 저장
List<int> currentDigitNumbers = new List<int>();
//이전에 조합된 숫자를 사용
foreach (int number in prevDigitNumbers)
{
//새로운 숫자를 이어붙일 때 현재 이미 사용 중인 숫자를 포함하지 않도록 함
string numStr = number.ToString();
List<int> usingNumbers = numberSet.ToList();
//기존 숫자에서 사용하던 숫자를 제외하고 추가
foreach (char c in numStr)
{
usingNumbers.Remove(int.Parse(c.ToString()));
}
//사용하기로 결정된 숫자만 기존 숫자 뒤에 이어붙힘
foreach (int n in usingNumbers)
{
int tempNum = int.Parse(numStr + n);
//중복되는 숫자는 포함하지 않음
if(!currentDigitNumbers.Contains(tempNum))
currentDigitNumbers.Add(tempNum);
}
}
_numbersList.AddRange(prevDigitNumbers);
prevDigitNumbers = currentDigitNumbers;
}
_numbersList.AddRange(prevDigitNumbers);
}
numberSet
에 저장prevDigitNumbers
에 이전에 사용한 숫자 조합을 넣고 이후 반복문에서 해당 숫자 뒤에 재료가 될 숫자를 선택해서 붙이는 방식으로 새로운 순열을 생성 string numStr = number.ToString();
List<int> usingNumbers = numberSet.ToList();
//기존 숫자에서 사용하던 숫자를 제외하고 추가
foreach (char c in numStr)
{
usingNumbers.Remove(int.Parse(c.ToString()));
}
_numbersList
에 추가 private static void InitPrimeNumberList(int maxNum)
{
for (var i = 2; i <= maxNum; i++)
{
if(IsPrime(i))
_primeNumberList.Add(i);
}
}
private static bool IsPrime(int number)
{
for (int i = 2; i <= Math.Sqrt(number); i++)
{
if (number % i == 0) return false;
}
return true;
}
_primeNumberList
를 작성return _numbersList.Intersect(_primeNumberList).Count();
_numbersList
의 각각의 값이 소수인지 판단하는 것이 무조건 최대값 이하의 전체 숫자에서 소수를 찾는 검사보다 검사량이 적으므로 현재 방법보다는 _numbersList
의 각 성분을 검사하며 소수인지 판단하는 것이 유리할 것으로 보임