5

У меня есть эта начальная строка.Оптимизация покрытия строк в Python

'bananaappleorangestrawberryapplepear' 

А также кортеж со строками:

('apple', 'plepe', 'leoran', 'lemon') 

Я хочу функцию так, что из исходной строки и кортежа со строками я получаю это:

'bananaxxxxxxxxxgestrawberryxxxxxxxar' 

Я знаю, как сделать это обязательно, найдя слово в исходной строке для каждого слова, а затем закодируйте символ по символу во всей исходной строке с замещенными словами.

Но это не очень эффективно и уродливо. Я подозреваю, что должен быть какой-то способ сделать это более элегантно, функционально, с помощью itertools или что-то еще. Если вы знаете библиотеку Python, которая может сделать это эффективно, сообщите мне об этом.

ОБНОВЛЕНИЕ: Джастин Пил указал на случай, который я не описал в своем первоначальном вопросе. Если слово «aaa» и «aaaaaa» находится в начальной строке, результат должен выглядеть так: «xxxxxx».

ответ

3
import re 

words = ('apple', 'plepe', 'leoran', 'lemon') 
s = 'bananaappleorangestrawberryapplepear' 

x = set() 

for w in words: 
    for m in re.finditer(w, s): 
     i = m.start() 
     for j in range(i, i+len(w)): 
      x.add(j) 

result = ''.join(('x' if i in x else s[i]) for i in range(len(s))) 
print result 

производит:

bananaxxxxxxxxxgestrawberryxxxxxxxar 
+0

Единственная проблема, с которой я вижу, - это следующий прецедент: одно из слов - «aaa» и строка s = 'aaaaa'. Этот метод даст результат «xxxaa», а не «xxxxx», потому что «finditer» находит следующее неперекрывающееся совпадение. Наверное, не придет, но это зависит от того, чего хочет OP. –

+0

Да, мне было непонятно, что должно произойти с перекрывающимися экземплярами слов. –

+0

@ Justin Я не думал об этом случае, но в случае строки «aaaaaa» слово «aaa» должно указывать «xxxxxx». Но это действительно угловой случай, я мог бы жить с «xxxaa», если что-то лучше. –

0
a = ('apple', 'plepe', 'leoran', 'lemon') 
b = 'bananaappleorangestrawberryapplepear' 

for fruit in a: 
    if a in b: 
     b = b.replace(fruit, numberofx's) 

Единственное, что вам нужно сделать сейчас, это определить, сколько X заменить на.

+4

Это не удастся, поскольку оно не гарантирует полного покрытия, например. «apple» и «plepe» перекрываются, но второй не обрабатывается. –

0
def mask_words(s, words): 
    mask = [False] * len(s) 
    for word in words: 
     pos = 0 
     while True: 
      idx = s.find(word, pos) 
      if idx == -1: 
       break 

      length = len(word) 
      for i in xrange(idx, idx+length): 
       mask[i] = True 
      pos = idx+length 

    # Sanity check: 
    assert len(mask) == len(s) 

    result = [] 
    for masked, c in zip(mask, s): 
     result.append('x' if masked else c) 

    return "".join(result) 
+0

Я не знаю, это то, что вы подразумеваете под «уродливым», но это достаточно быстро и понятно. Если вы обрабатываете очень большие строки с небольшим количеством ударов, вы можете немного уменьшить использование памяти, сохранив диапазоны для маскирования, а не полный массив, но производительность здесь кажется разумной. –

+0

'pos = idx + length' неверно. В позицию должно быть добавлено только 1, иначе она не будет использоваться с помощью 'yyy' и' yyyyy'. –

1

Вот еще один ответ. Возможно, существует более быстрый способ заменить буквы x, но я не думаю, что это необходимо, потому что это уже довольно быстро.

import re 

def do_xs(s,pats): 
    pat = re.compile('('+'|'.join(pats)+')') 

    sout = list(s) 
    i = 0 
    match = pat.search(s) 
    while match: 
     span = match.span() 
     sout[span[0]:span[1]] = ['x']*(span[1]-span[0]) 
     i = span[0]+1 
     match = pat.search(s,i) 
    return ''.join(sout) 

txt = 'bananaappleorangestrawberryapplepear' 
pats = ('apple', 'plepe', 'leoran', 'lemon') 
print do_xs(txt,pats) 

В принципе, я создаю шаблон регулярного выражения, который будет соответствовать любому из шаблонов ввода. Затем я просто возобновляю поиск, начиная 1 после начальной позиции последнего совпадения. Может возникнуть проблема, хотя, если у вас есть один из шаблонов ввода, это префикс другого шаблона ввода.

+0

Если вы знаете, как позаботиться о кромке «xxxa», сообщите мне свое решение. –

1

Если предположить, что мы ограничены работать без STDLIB и другого импорта:

s1 = 'bananaappleorangestrawberryapplepear' 
t = ('apple', 'plepe', 'leoran', 'lemon') 
s2 = s1 

solution = 'bananaxxxxxxxxxgestrawberryxxxxxxxar' 

for word in t: 
    if word not in s1: continue 
    index = -1 # Start at -1 so our index search starts at 0 
    for iteration in range(s1.count(word)): 
     index = s1.find(word, index+1) 
     length = len(word) 
     before = s2[:index] 
     after = s2[index+length:] 
     s2 = before + 'x'*length + after 

print s2 == solution 
+0

Хорошо, ограничение встроенных функций не было частью проблемы, потому что OP, упомянутый с использованием itertools (что, я сомневаюсь, все равно будет работать, поскольку у нас есть две ссылочные строки). Ну что ж. – eternicode

+0

Вы знаете что-нибудь в stdlib, чтобы сделать это легко? –

+0

Возможно, вы сможете сделать это короче с re. В противном случае нет. – eternicode

1
>>> string_ = 'bananaappleorangestrawberryapplepear' 
>>> words = ('apple', 'plepe', 'leoran', 'lemon') 
>>> xes = [(string_.find(w), len(w)) for w in words] 
>>> xes 
[(6, 5), (29, 5), (9, 6), (-1, 5)] 
>>> for index, len_ in xes: 
... if index == -1: continue 
... string_ = string_.replace(string_[index:index+len_], 'x'*len_) 
... 
>>> string_ 
'bananaxxxxxxxxxgestrawberryxxxxxxxar' 
>>> 

Есть, конечно, более эффективные способы, но преждевременная оптимизация есть корень всех зол.