odzさんのSuffix Array を三分間ハッキングで高速化してみる。
import sys
def build_suffix_array(text, indices = None):
if indices is None:
indices = range(len(text))
length = len(text)
def suffix_compare(a, b):
m = min(a, b)
for i in xrange(0, length - m + 63, 64):
c = cmp(text[a+i:a+i+64], text[b+i:b+i+64])
if c: return c
return 0
indices.sort(suffix_compare)
return indices
def read_file(filename):
fp = file(filename)
try:
return fp.read()
finally:
fp.close()
def suffix(text, index, max_length):
last = text.find('\n', index, index + max_length)
if last != -1:
return text[index:last]
else:
return text[index:index + max_length]
def main(args, options):
if args:
text = ''.join(read_file(f) for f in args)
else:
text = sys.stdin.read()
if options.encoding:
text = text.decode(options.encoding)
suffix_array = build_suffix_array(text)
for i in suffix_array:
sys.stdout.write('%d\t%s\n' % (i, suffix(text, i, 10).encode('utf-8')))
if __name__ == '__main__':
from optparse import OptionParser
parser = OptionParser()
parser.add_option('-e', '--encoding', dest='encoding',
help='input encoding', metavar='encoding')
(options, args) = parser.parse_args()
main(args, options)
元ネタの問題は文字列のコピーを作りすぎるところにあるので、 小部分の比較に置き換えれば十分に速くなる。 Pythonでは文字列の範囲外は空文字列にしてくれるので、 明示的なチェックはこの場合不要。
こういう姑息な最適化をするとPsycoがさっぱり効かなくなるのだが、 Psycoを使うより速くなるので、まあいいとします。
そもそもの問題はコピーを作らずに比較できないことなのだけれど、 Python 3000ではsubstringを共有させるようにしようとか、 そういう話も持ち上がっていたみたい。 結局どうなるかは覚えてない。