PFIのサマーインターンの問題を考えてみた
http://research.preferred.jp/2011/07/intern2011_problem/
長さnの文字列中で出現回数が最大の文字をO(n)時間で答えるプログラムを書いてください。 但し、出現回数が最大の文字の出現回数はn/2より大きいとします。 条件として、文字列以外に利用できるバッファサイズはnに依存しない定数であり、文字種類数は可変(最大n)であるとします。
元のブログでも言われているように条件があるので
- 単純に各文字毎に頻度を数えるのにはバッファサイズが定数ですので記録できません
- 文字をソートするのもO(n log n)時間必要
ということになります。
私がこの問題で気にしたのはこれ。
- 出現回数が最大の文字の出現回数はn/2より大きい
この条件から発展させてみる。
- 出現回数が最大の文字は少なくとも (n/2) + 1 回出現する
- (n/2) はInt型の割り算のように小数点以下は切り捨て
- n が偶数のとき、どこかに出現回数が最大の文字のペアが少なくとも1回現れる
- n が奇数のとき、出現回数が最大の文字のペアが現れる、現れなければ末尾の文字が出現回数が最大である。
3,4に関しては正直なところ経験的に考えたものです*1。
反例はないかなーと言われましたが、できても腑に落ちないところ*2。
実際にRubyで書いたコードが以下のものです。
# compare two char of input, # and keep the number of times when 2 char is equal def compare2chars(str) eql_count = 0 now_char = 0 lambda do |n| if str[n] == str[n+1] if str[n] == now_char eql_count += 1 else if eql_count == 1 now_char = 0 eql_count -= 1 elsif eql_count == 0 now_char = str[n] eql_count += 1 else eql_count -= 1 end end end return now_char, eql_count end end # search the char that appears most def search_char(str) now_char = 0 eql_count = 0 compare_chars = compare2chars(str) str_size = str.size count = 0 if (str_size == 2 && str[0] == str[1]) || str_size == 1 return str[0].chr elsif str_size % 2 == 0 0.step(str_size - 1, 2) do |n| now_char, eql_count = compare_chars.call(n) end elsif str_size % 2 == 1 0.step(str_size - 2, 2) do |n| now_char, eql_count = compare_chars.call(n) end if eql_count == 0 now_char = str[str_size-1] end else return nil end str.each_char do |ch| count += 1 if ch == now_char.chr end if count > str_size / 2 return now_char.chr else return nil end end if ARGV[0] p search_char(ARGV[0]) else p search_char(STDIN.gets.chomp) end
変数
- now_char: 仮の出現回数が最大の文字
- eql_count: 仮の出現回数が最大の文字によるペアが等しい回数
動作
奇数、偶数の場合に分けて2文字ずつ判定(compare2chars)
- 2文字が等しくeql_countが0のとき、その文字をnow_charとしeql_countをインクリメント
- now_charと違うペアが来たら回数をデクリメント
偶数・奇数ともに返ってきたnow_charが出現回数が最大の文字
- ただし奇数の場合で、eql_countが0のときは末尾文字が出現回数が最大の文字である
最後にnow_charが本当に (n/2) 回より多く出現しているかチェック
step文で O(n/2)、最後のチェックで O(n) なので O(n) の条件は満たしているはず。
最後に
色々書いたわけですが、選考は落ちました*3。
精進します。。