CodeIQ:140問目!素数列から抜き出してつぶやこう?

私自身が表題の問題を解いた時のプログラムについて解説します。
問題の詳細は「140問目!素数列から抜き出してつぶやこう?」(CodeIQ)を参照してください。

問題の概要

問題を引用します。
今週のアルゴリズムも140問目!
「140」といえばTwitterにおけるつぶやきの文字数の上限です。

m 以上 n 以下の素数を一列に並べ、その中から連続した数字列を最長140文字で抜き出したとき、最初と最後の数字が同じで、含まれる数の和が最大になるものを求め、その和を出力してください。

例えば、m = 5, n = 30 のとき、
57111317192329
という数字列の中から、最初と最後の数字が同じで、長いものを抜き出すと、含まれる数の和は
7111317 → 21
1113171 → 15
3171923 → 26
92329 → 25
232 → 7
となりますので、最大なのは26です。

標準入力から m と n がスペース区切りで与えられるとき、和の最大値を求め、標準出力に出力してください。
なお、m と n は 1 < m < n < 100000を満たす整数とします。

【入出力サンプル】
標準入力
5 30

標準出力
26

私のプログラム

Rubyで解答しています。

#!/usr/bin/ruby
require 'prime'

def makeNum(m,n)
	ret = ""
	Prime.each(n){|i|
		next if i < m
		ret += i.to_s
	}
	return ret.split("").map{|a| a.to_i}
end

def solve(m,n)
	full = makeNum(m,n)

	mx = 0
	for i in 0...full.size
		pos = full[i,140].rindex(full[i])
#		printf("%s, find=%d, pos=%d\n", full[i,140].to_s, full[i], pos)
		next if pos == 0
		num = full[i, pos+1]
#		p num

		s = num.reduce(:+)
		mx = s if s > mx
	end

	return mx
end

# main
while line = gets
	line.strip!
	if line.empty? then next end

	m,n = line.split.map{|a| a.to_i}

	p solve(m,n)
end

解説

今回はメモ化再帰ではありません。
うまい方法が思いつかなかったので総当たりです。

考え方

とりあえず数字列を作ります。
0番目から後ろに向かって先頭位置をずらしながら順番に探索します。
i番目の数を先頭とした場合、i+140番目の数字から先頭に向かってi番目と同じ数を探し、その間の数の合計を計算します。
これを最後までやって一番合計が大きかったものを選ぶという単純なロジックです。

main

入力値を数値にしてsolve()に渡し、結果を印字します。

solve(m,n)

makeNum()でm以上n以下の素数を繋げた数字列(配列)fullを取得します。

fullの先頭から最後の一つ前までを開始位置として順番にチェックします。最後の一つ手前までなのは「連続した数」が評価対象なので数字1つだけの場合はチェックしなくて良いからです。
posはi番目を先頭とした時にi+140番目から手前に向かってi番目の数と同じ値が最初に出現する位置です。これはArray#rindex()で単純に探索しています。
pos=0の場合、つまり先頭まで同じ数がなかった場合は評価対象にならないので無視します。
そうでなければその間の値の合計を求めます。num=full[i,pos+1]と1足しているのはpos = full[i,140].rindex(full[i])が返すのがiの場所を0とした場合のインデックスなので要素数の場合+1する必要があるためです。例えば[1,2,1]だとインデックスは2が返りますが、要素数は3になるということです。

あとは合計値が過去の合計値より大きければその値を候補値として記録し、最後までチェックした時に残った候補値が答えになります。

makeNum(m,n)

m以上n以下の素数を繋げた数字列を作成して返します。
Rubyには素数ライブラリがあるので簡単です。
一旦文字列で連結してから数値の配列に変換しています。

雑感

これ、もっと効率の良い方法はあるのでしょうか?
140個までという条件が以外に面倒で総当たりより良い方法を思いつきませんでした。