Kickstart

Google Kickstart 2018 Round A 풀이 (코드포함)

이 포스팅에서는 Google Kickstart 2018 Round A 풀이와 코드를 제공합니다. 본 풀이는 공식적인 풀이와 다를 수 있으며, 온라인 채점 도구를 통과한 소스코드를 제공합니다. 대회에 대한 자세한 사항은 공식 홈페이지를 참조하시기 바랍니다.

Prob A ‘Even Digits’

문제 링크

문제 설명

모든 자릿수가 짝수인 수들 중에서 주어진 N과 가장 가까운 수와 N의 차의 절대값을 출력하는 문제

문제 풀이

아래 코드는 이분 검색을 통해 문제를 해결한 것이다. 모든 자릿수가 짝수인 수는 0, 2, 4, 6, 8로만 구성된다. 5진법을 이용한다면, 5진법으로 나타낸 수의 각 자릿수값 0, 1, 2, 3, 4를 0, 2, 4, 6, 8로 치환시킬 수 있다. 5 ** 19는 5진법으로 19자리이기 때문에 문제의 조건을 만족하는 10진수에 19자리까지 대응시킬 수 있다. 이분 검색의 범위를 나타내는 left와 right는 실제 10진수 값이 아니라, 문제의 조건을 만족하는 10진수들을 오름차순으로 정렬했다고 가정할 때 그 수의 인덱스를 나타내는 값이라고 생각하면 된다. 이 인덱스를 이분 검색으로 줄여나가며 왼쪽과 오른쪽에서 주어진 수 N과 가장 가까운 수를 각각 찾아서 그 중 더 가까운 것과 N의 차의 절대값을 출력한다.

def TenToFive(num):
  if num == 0:
    return '0'
  else:
    if num // 5 != 0:
      return TenToFive(num // 5) + str(num % 5)
    else:
      return str(num % 5)

def FiveToTen(num):
  V = ['0', '2', '4', '6', '8']
  return int("".join(V[int(i)] for i in str(num)))

def Do(left, right):
  global N, Left, Right
  if left > right:
    return
  mid = (left + right) // 2
  Num = FiveToTen(TenToFive(mid))

  if Num > N:
    Right = min(Right, Num)
    Do(left, mid - 1)
  elif Num < N:
    Left = max(Left, Num)
    Do(mid + 1, right)
  else:
    Right = min(Right, Num)
    Left = max(Left, Num)

T = int(input())
for tt in range(T):
  N = int(input())
  Left, Right = -1, FiveToTen(TenToFive(5 ** 19))
  Do(0, 5 ** 19)
  print ("Case #" + str(tt + 1) + ":", min(N - Left, Right - N))

Prob B ‘Lucky Dip’

문제 링크

문제 설명

가방 안에 N개의 아이템이 있고 각 아이템은 V_i 만큼의 가치를 가진다. 가방에 손을 집어넣어 무작위로 아이템을 뽑는데, K + 1번까지 시도할 수 있다. 재도전은 현재 뽑은 아이템을 다시 가방에 넣은 후에 진행한다. 가장 마지막 도전에서 뽑은 아이템의 가치가 최종 스코어가 된다. 최종 스코어의 기댓값을 최대화 시켰을 때 그 값은 무엇인가.

문제 풀이

매 단계에서 다시 주사위를 던질지 말지를 결정하는 방식은 다음과 같다.
현재 꺼낸 아이템의 가치가 재시도의 기댓값보다 크다면 Stop, 아니라면 Go하는 것이다.

다음 단계의 기댓값이 Next일 때, 현재 단계의 기댓값은
count(Next보다 작은 가치의 아이템 수) / N x Next + sum(Next보다 큰 아이템 가치) / N 이다.

def getSum(arr):
  Sum = [0] * len(arr)
  Sum[0] = arr[0]
  for i in range(1, len(arr)):
    Sum[i] = Sum[i - 1] + arr[i]
  return Sum
    
def findIndexOfBiggerElement(arr, val):
  left = 0
  right = len(arr) - 1
  check = len(arr)
  while left <= right:
    mid = (left + right) // 2
    if arr[mid] >= val:
      check = min(check, mid)
      right = mid - 1
    else:
      left = mid + 1
  return check

T = int(input())
for tt in range(T):
    N, K = [int(x) for x in input().split()]
    Nums = [int(x) for x in input().split()]
    Nums.sort()
    Sum = [0] + getSum(Nums)
    
    # Calc ExpectedValue
    ExpectedValue = sum(float(num) for num in Nums)/float(N)
    
    # Improve Expected Value over try
    Next = ExpectedValue
    for i in range(K):
      Temp = 0.0
      Idx = findIndexOfBiggerElement(Nums, Next)
      LocalSum = Sum[N] - Sum[Idx]
      Temp = float(LocalSum)/float(N) + float(Idx) * float(Next) / float(N)
      Next = Temp
    
    print ("Case #" + str(tt +1) + ":", Next)

Prob C ‘Scrambled Words’

문제 링크

문제 설명

임의로 생성된 문자열과 여러 단어가 주어진다. 주어진 단어 중에서 문자열의 부분문자열과 일치하는 것의 갯수를 출력하는 문제다.
다만 여기에선 문자열 비교 방식이 좀 다르다. 두 문자열의 비교는 처음과 끝 알파벳이 같고, 단어를 구성하는 각 알파벳의 수가 동일하면 같은 것으로 간주한다. 예를 들어 abcde는 acbde나 adcbe와도 같은 것이다.

문제 풀이

우선 어떤 단어가 문자열에 포함되는지 쉽게 판별하는 방법을 찾아낸다. 문자는 26개이고 앞과 끝 글자와 구성만 비교하기 때문에 배열을 사용한다. 길이 26의 배열에 각 알파벳이 출현하는 빈도를 기록하고, 앞과 끝 글자만 기록한다. A[i : i + len(word)]에 속하는 부분문자열에서 A[i + 1 : i + 1 + len(word)]에 속하는 부분문자열로 넘어갈 때는 기존 빈도에서 A[i]에 해당하는 빈도를 하나 감소시키고 A[i + 1 + len(word)]에 해당하는 빈도를 하나 증가시킨다. 그리고 앞과 끝 글자만 업데이트하면 된다.

이 값들을 모두 “,”로 합쳐서 HashString을 만든다. 딕셔너리를 사용해서 Dic[HashString] = True로 만들고, 주어진 단어로 만든 HashString이 Dic에 포함되어 있는지 판별한다.

전체 문자열의 길이는 10^5를 넘지 않는다. 이 말은 서로 다른 단어의 길이가 많아야 500개 이하로 존재한다는 말이다. 각 길이에 대해서 딕셔너리는 한 번만 만들면 되기 때문에 딕셔너리는 500개를 넘지 않는다. 각 단어를 길이순으로 정렬해서, 같은 길이들에 대해서는 기존에 만들어진 딕셔너리에 대해 포함되는 HashString의 갯수를 세면 되겠다.

배열 때문인지 Large Case에서는 Error가 난다.

def genDic(sentence, sentenceVec, length):
  Dic = {}
  Arr = [0] * 26
  
  # Initial Array
  s1, s2 = sentence[0], sentence[length - 1]
  for i in range(length):
    Arr[sentenceVec[i]] += 1
  Dic[getHashKey(s1, s2, Arr)] = 1

  # Iterate
  for i in range(0, len(sentenceVec) - length):
    Arr[sentenceVec[i]] -= 1
    Arr[sentenceVec[i + length]] += 1
    s1, s2 = sentence[i + 1], sentence[i + length]
    Dic[getHashKey(s1, s2, Arr)] = 1
  return Dic
  
def getHashKey(s1, s2, Arr):
  return ",".join([s1,s2] + [str(x) for x in Arr])

def chrToInt(sentence):
  vec = [0] * len(sentence)
  for i in range(len(sentence)):
    vec[i] = ord(sentence[i]) - 97
  return vec

def wordToVec(word):
  vec = [0] * 26
  for i in range(len(word)):
    vec[ord(word[i]) - 97] += 1
  return vec

def genSentence(s1, s2, N, A, B, C, D):
  A, B, C, D = int(A), int(B), int(C), int(D)
  sentence = [0] * N
  sentence[0], sentence[1] = s1, s2
  x1, x2 = ord(s1), ord(s2)
  for i in range(2, N):
    x1, x2 = x2, (A * x2 + B * x1 + C) % D
    sentence[i] = chr(97 + (x2 % 26))
  return "".join(sentence)

T = int(input())
for tt in range(1, T + 1):
  # Get words and preprocess
  wordCnt = int(input())
  words = raw_input().split()
  wordVectors = []
  wordLength = []
  for i in range(len(words)):
    length = len(words[i])
    wordLength.append([length, i])
  wordLength.sort()
  
  for i in range(len(wordLength)):
    word = words[wordLength[i][1]]
    length = wordLength[i][0]
    vector = wordToVec(word)
    wordVectors.append([length, getHashKey(word[0], word[length - 1], vector)])

  # Generate String
  s1, s2, N, A, B, C, D = raw_input().split()
  N = int(N)
  sentence = genSentence(s1, s2, N, A, B, C, D)
  sentenceVec = chrToInt(sentence)

  prevLength = -1
  cnt = 0
  for i in range(len(wordVectors)):
    vector = wordVectors[i]
    length = vector[0]
    HashString = vector[1]
    # Get Vector Dictionary for new length
    if prevLength != length:
      Dic = genDic(sentence, sentenceVec, length)
      prevLength = length
    if HashString in Dic:
      cnt += 1
  print "Case #" + str(tt) + ": " + str(cnt)
    
admin

Recent Posts

2024년 11월 17일 일요일 – 서울 생활 129주차

괌여행 아내와 함께 괌으로 여행을 갔다. 출산 이후로 둘만 가는 첫 여행이다. 하와이가 일본인들이 많이…

3일 ago

2024년 11월 3일 일요일 – 서울 생활 127주차

반복적인 일상 매일 아침 일어나 회사에 출근하고, 저녁을 먹고 돌아오는 일상의 반복이다. 주말은 가족을 보러…

3주 ago

2024년 10월 6일 일요일 – 서울 생활 123주차

Planning A well-structured plan has the following characteristics:   First, the ultimate vision you aim to…

2개월 ago

2024년 9월 29일 일요일 – 서울 생활 122주차

English The most common problem for English learners like myself is that we often use…

2개월 ago

2024년 9월 22일 일요일 – 서울 생활 121주차

추석 추석을 맞아 친가 외가 처가 어르신들을 모두 뵙고 돌아왔다. 아이가 태어난지는 좀 됐지만 한국에서…

2개월 ago

2024년 9월 8일 일요일 – 서울 생활 119주차

주말 이번주는 아내가 올라와서 주말을 함께 보냈다. 둘만 밥도 먹고 뮤지컬도 보고, 모임도 나가고 집에서…

2개월 ago

This website uses cookies.