Original

If the numbers 1 to 5 are written out in words: one, two, three, four, five, then there are 3 + 3 + 5 + 4 + 4 = 19 letters used in total.

If all the numbers from 1 to 1000 (one thousand) inclusive were written out in words, how many letters would be used?

NOTE: Do not count spaces or hyphens. For example, 342 (three hundred and forty-two) contains 23 letters and 115 (one hundred and fifteen) contains 20 letters. The use of “and” when writing out numbers is in compliance with British usage.

和訳

1 から 5 までの数字を英単語で書けば one, two, three, four, five であり、全部で 3 + 3 + 4 + 4 + 5 = 19 の文字が使われている。

では 1 から 1000 (one thousand) までの数字をすべて英単語で書けば、全部で何文字になるか。

注: 空白文字やハイフンを数えないこと。例えば、342 (three hundred and forty-two) は 23 文字、115 (one hundred and fifteen) は20文字と数える。なお、”and” を使用するのは英国の慣習。

当てにならないソースコード(C#)

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
using System;
using System.Collections.Generic;

namespace ProjectEuler {
    class Problem17 {
        public Problem17() {
            Console.WriteLine(this.ToString());
            int answer = 0;
            for (int i = 1; i <= 1000; i++) {
                answer += Count(i);
            }
            Console.WriteLine("> " + answer);
        }
        int Count(int n) {
            if (n < 100) { // 1 <= n <= 99
                return CountLetters_LessThan100(n);
            }
            return CountLetters_Over100(n);
        }
        int CountLetters_LessThan100(int n) {
            if (n <= 20) {
                return number[n].Length;
            }
            if (n < 100) {
                int n1 = n % 10;
                if (n % 10 == 0) {
                    return number[n].Length;
                }
                int n10 = n - n1;
                return number[n10].Length + number[n1].Length;
            }
            return 0;//error
        }
        int CountLetters_Over100(int n) {
            if (n < 1000) { // 100 <= n <= 1000
                int n1   = n % 10,
                    n11  = n % 100,
                    n10  = n11 - n1,
                    n100 = n / 100;
                if (n11 == 0) {
                    return number[n100].Length + number[100].Length; // 100,200...
                }
                return number[n100].Length + number[100].Length + and
                    + CountLetters_LessThan100(n % 100);
            }
            if (n == 1000) {
                return number[1000].Length;
            }
            return 0;
        }
        const int and = 3;
        Dictionary<int, string> number = new Dictionary<int, string>{
            {1,"one"},
            {2,"two"},
            {3,"three"},
            {4,"four"},
            {5,"five"},
            {6,"six"},
            {7,"seven"},
            {8,"eight"},
            {9,"nine"},
            {10,"ten"},
            {11,"eleven"},
            {12,"twelve"},
            {13,"thirteen"},
            {14,"fourteen"},
            {15,"fifteen"},
            {16,"sixteen"},
            {17,"seventeen"},
            {18,"eighteen"},
            {19,"nineteen"},
            {20,"twenty"},
            {30,"thirty"},
            {40,"forty"},
            {50,"fifty"},
            {60,"sixty"},
            {70,"seventy"},
            {80,"eighty"},
            {90,"ninety"},
            {100,"hundred"},
            {1000,"onethousand"}
        };
    }
}


1から1000まで順番通りに文字数を数えています。
無駄が多い分、汎用的だと思います。
使い捨てのコードと割り切るなら、単語毎に出現回数を計算した方が早いです。