Problem 98
CAREという単語の文字をそれぞれ1,2,9,6に置き換えることにより、平方数1296 = 362を成す。注目すべきことは、同じ数字の置き換えによって、アナグラムのRACEもまた平方数9216 = 962を成すことである。CARE(とRACE)を平方アナグラム語と呼び、さらに0で始まるのは許さない、違う文字が同じ数字に置き換えられることも許さない。
(中略)平方アナグラム語のペアを全て見つけよ(回文語は自分自身のアナグラムになっている、とは考えない)。
このようなペアの要素を成す最大の平方数はなにか。
http://projecteuler.net/index.php?section=problems&id=98
まず、各単語をコード順にソートして辞書に入れます。例えば、
dic["ACER"] = [ "CARE", "RACE" ]
要素が2つ以上ある場合に、各々のペアの単語の両方が平方になる置き換えがあるか調べます。例えば、
[ "CARE", "RACE", "ACER" ]
と3つの要素があれば、3C2 = 3つのペアを調べます。
そして、桁数が合うすべての平方数を一方の単語にあてはめて、同じ置き換えでもう一つの単語が平方数になるかを調べます。
from itertools import ifilter, combinations from math import sqrt def digits(n): if n == 0: return [ ] else: a = digits(n / 10) a.append(n % 10) return a def numerize(a): return reduce(lambda x, y: x * 10 + y, a) def is_square(n): m = int(sqrt(n)) return m * m == n def not_duplicated(a): b = [ False ] * 10 for e in a: if b[e]: return False else: b[e] = True return True def make_dic(a): def sort(s): return "".join(sorted(s[k] for k in range(len(s)))) def add(a, s): if a in m: m[a].append(s) else: m[a] = [s] m = { } for s in a: add(sort(s), s) return m def index(a, n): t = () for k in range(len(a)): t = t + (k,) return t def is_match(s, a): c = [ ' ' for n in range(10) ] for k in range(len(s)): if c[a[k]] == ' ': c[a[k]] = s[k] elif c[a[k]] != s[k]: return False for k in range(1, len(s)): for l in range(k): if s[k] == s[l] and a[k] != a[l]: return False return True def match_square(pair): s1, s2 = pair convert = [ s2.index(c) for c in s1 ] l = len(s1) begin = int(sqrt(10**(l-1))) end = int(sqrt(10**l-1)) for a in ifilter(lambda a: is_match(s1, a), (digits(k * k) for k in xrange(end, begin, -1))): n = numerize(map(lambda k: a[convert[k]], range(l))) if n >= 10 ** (l-1) and is_square(n): return max(n, numerize(a)) return 0 file = open("words.txt") a = map(lambda s: s[1:-1], file.read().split(',')) file.close() b = filter(lambda s: len(s) > 1, make_dic(a).itervalues()) print reduce(max, (match_square(d) for c in b for d in combinations(c, 2)))