오일러 프로젝트 22

오일러 프로젝트의 22번 문제는 특정한 문자열을 정해진 규칙에 따라 점수로 환원하고 그 합계를 계산하는 것이다.

여기 5천개 이상의 영문 이름들이 들어있는 46KB짜리 텍스트 파일 names.txt 이 있습니다 (우클릭해서 다운로드 받으세요). 이제 각 이름에 대해서 아래와 같은 방법으로 점수를 매기고자 합니다. 먼저 모든 이름을 알파벳 순으로 정렬합니다. 각 이름에 대해서, 그 이름을 이루는 알파벳에 해당하는 숫자(A=1, B=2, …, Z=26)를 모두 더합니다. 여기에 이 이름의 순번을 곱합니다. 예를 들어 “COLIN”의 경우, 알파벳에 해당하는 숫자는 3, 15, 12, 9, 14이므로 합이 53, 그리고 정렬했을 때 938번째에 오므로 최종 점수는 938 × 53 = 49714가 됩니다. names.txt에 들어있는 모든 이름의 점수를 계산해서 더하면 얼마입니까? ::

http://euler.synap.co.kr/prob_detail.php?id=21

접근 및 풀이

모든 글자는 대문자이며, 알파벳 글자 순서에 따라 각 글자는 점수값을 갖는다. 이를 위해서 ABCD.....Z 에 이르는 문자열에 각 점수를 대입한 사전을 만들어서 글자의 점수를 구해도 되고, 아니면 ord() 함수를 이용하는 방법도 있다. ‘A’의 아스키코드가 65이므로 ord(x) - 64를 통해서 특정 알파벳의 점수값을 알 수 있다.

사실 문제 자체는 엄청 간단한 내용이라, 그외에 따로 코멘트 할 일은 없을 듯 하고, 여기서는 파일을 다운로드 하지 않고 http로 액세스해서 값을 계산하는 코드를 작성했다. (Python 3.6)

from urllib.request import urlopen

with urlopen('http://euler.synap.co.kr/files/names.txt') as f:
  names = sorted(x.strip('"') for x in f.read().decode().split(','))
  print(sum((sum(ord(c) - 64 for c in word)) * (i+1)\
             for i, word in enumerate(names)))

보너스 : Swift 풀이

// Swift 4
import Foundation

main: do {
  guard let url = URL(string: "http://synap.euler.co.kr/files/names.txt"),
        let contents = try? String(contentsOf: url, encoding: .utf8)
  else { break main }
  let names = contents.split(separator: ",").sorted{ $0 < $1 }
  var result = 0
  for (i, word) in names.enumerated() {
    result += getScore(word) * (i + 1)
  }
  print(result)
}

func getScore(_ s: String) -> Int {
  let score = s.utf8.map{ (x) -> Int in 
    let y = Int(x)
    return (65...91) ~= y ? y - 64 : 0
  }
  return score.reduce(0, +)
}

참고로 문자열 타입의 split(separator:)는 Swift4에서 추가된 API이며, Xcode8.3 이하에서는 Foundation 내에 정의된 components(separatedBy:_)를 사용하면 된다.