Эта версия должна быть линейной по длине строки и должна подойти, если последовательности не слишком повторяются (в этом случае вы можете заменить рекурсию циклом while).
def find_all(st, substr, start_pos=0, accum=[]):
ix = st.find(substr, start_pos)
if ix == -1:
return accum
return find_all(st, substr, start_pos=ix + 1, accum=accum + [ix])
Понимание списка bstpierre является хорошим решением для коротких последовательностей, но, похоже, имеет квадратичную сложность и никогда не заканчивается на длинном тексте, который я использовал.
findall_lc = lambda txt, substr: [n for n in xrange(len(txt))
if txt.find(substr, n) == n]
Для случайной строки нетривиальной длины две функции дают одинаковый результат:
import random, string; random.seed(0)
s = ''.join([random.choice(string.ascii_lowercase) for _ in range(100000)])
>>> find_all(s, 'th') == findall_lc(s, 'th')
True
>>> findall_lc(s, 'th')[:4]
[564, 818, 1872, 2470]
Но квадратичная версия примерно в 300 раз медленнее
%timeit find_all(s, 'th')
1000 loops, best of 3: 282 µs per loop
%timeit findall_lc(s, 'th')
10 loops, best of 3: 92.3 ms per loop