Python 3: совершенный алфавитный заказ
Цель кода - найти самую длинную алфавитную подстроку в строке.
s = 'xyzbcdezzz'
longest_string = ''
current_string = ''
stringcount = 0
for n in range (len(s) - 1):
if s[n] <= s[n+1]:
current_string += (s[n]+s[n+1])
stringcount += 1
print('current string:', stringcount, current_string)
elif s[n] > s[n+1]:
if len(current_string) > len(longest_string) :
longest_string = current_string
current_string = ''
stringcount = 0
print('the longest string checked is:', longest_string, ', count reset')
if len(current_string) == len(longest_string):
print (current_string, longest_string)
if len(current_string) > len(longest_string):
print (current_string)
if len(longest_string) > len(current_string):
print(longest_string)
Когда я запускаю этот код, он дает "abbccd" как самую длинную подстроку, когда она фактически "abcd". Это происходит потому, что он проверяет символ a, сравнивая его со следующим в последовательности, а затем добавляет a в b, давая "ab". Затем он проверяет b, сравнивая с c и добавляет bc вместе, а затем добавляет "bc" в "ab".
Чтобы исправить это, я пытался заставить цикл пропустить следующий символ, если он уже в алфавитном порядке, и проверить следующий, увеличив значение "n" после выполнения условия, но это не означает, похоже, ничего не делает.
Приветствуются советы, советы, исправления и суровая критика.
EDIT: Кажется, я ввел некоторых из вас в заблуждение, поэтому извиняюсь. Я имел в виду, что если у меня есть строка, она извлекает самую длинную подстроку в алфавитном порядке. В случае xyzbcdezzz он будет извлекать "bcdezzz", потому что это самая длинная по алфавиту последовательность подстановок, а не bcde. Проблема с моим текущим кодом заключается в том, что он дает bccddeezzzzz. Если бы я мог пропустить один цикл, когда условие first if равно true, то я думаю, что это может работать в моем коде.
Ответы
Ответ 1
После вашего редактирования становится понятнее, каков был ваш вопрос. Я изменил ваш код как можно меньше, чтобы показать вам, откуда появилась ошибка в вашем решении.
Вот код:
s = 'xyzbcdezzz'
longest_string = ''
current_string = ''
for n in range(len(s)):
if len(current_string) == 0 or current_string[-1] <= s[n]:
current_string += s[n]
print('current string:', len(current_string), current_string)
else:
if len(current_string) > len(longest_string):
longest_string = current_string
current_string = s[n]
print('the longest string checked is:', longest_string, ', count reset')
if len(current_string) > len(longest_string):
longest_string = current_string
print(longest_string)
проблематичная часть принимала сразу 2 символа в
if s[n] <= s[n+1]:
current_string += (s[n]+s[n+1])
заменив его на
if len(current_string) == 0 or current_string[-1] <= s[n]:
current_string += s[n]
вы добавите текущую строку точно, если добавление будет действительным (последний char current_string[-1]
и добавленный wannabe s[n]
в порядке)
Элемент elif упрощен, чтобы не проверять s[n]
и s[n+1]
, потому что он не отражает то, что мы пытаемся сделать: нам все равно, если символы не упорядочены во всей строке s
, мы заботимся о нашей текущей строке (эта логика улавливается выражением if выше, а еще будет посещаться только в случае возникновения проблемы)
поэтому изменение здесь
elif s[n] > s[n+1]:
if len(current_string) > len(longest_string) :
к
else:
if len(current_string) > len(longest_string):
longest_string = current_string
current_string = s[n]
добавление нового победителя, если необходимо, и сброс текущей строки в char, который не был в порядке
Последний набор ifs проверяет случай, когда current_string
заканчивается на последнем char s
, хотя это правильно, было бы менее отвлекающим, если вы добавите проверку после цикла и напечатаете только longest_string
if len(current_string) > len(longest_string):
longest_string = current_string
print(longest_string)
Таким образом, выход является первой допустимой длинной строкой в каждом случае, а не двумя разными самыми длинными, когда один из них находится в хвосте строки
Ответ 2
TL; DR: последний код после редактирования решает проблему
Это вариант самой длинной общей проблемы подстроки.
def longestSubstring(string1, string2):
answer = ""
len1, len2 = len(string1), len(string2)
for i in range(len1):
match = ""
for j in range(len2):
if (i + j < len1 and string1[i + j] == string2[j]):
match += string2[j]
else:
if (len(match) > len(answer)): answer = match
match = ""
return answer
alphabets = "abcdefghijklmnopqrstuvwxyz"
s = 'jamabcdaskl'
print('longest substring:', longestSubstring(s,alphabets))
Кредиты на этот пост для подпрограммы.
Edit:
Кажется, что приведенный выше код не работает для всех случаев, поэтому мне пришлось перепроектировать функцию.
def longestAlphaSubstring(str2):
str1 = "abcdefghijklmnopqrstuvwxyz"
longest = ""
for i in range(len(str1)+1):
if str1[:i] in str2 and len(str1[:i])>len(longest):
longest = str1[:i]
return longest
print(longestAlphaSubstring('jamabcdaskl'))
print(longestAlphaSubstring('asdklfjalkdfjabcdefghijklmnopqrstuvwxyzaaabcasdkfjl;kasdf'))
Вывод:
abcd
abcdefghijklmnopqrstuvwxyz
Это работает в предположении, что подстрока всегда должна начинаться с a
. Это повторяется через каждую возможную подстроку из "a", "ab", "abc",..., вплоть до полной строки алфавитов, а затем сохраняет самую длинную подстроку, встречающуюся в чеке.
Для полноты, вот код, который будет работать для любой самой длинной общей подстроки:
def longestSubstring(str1, str2):
longest = ""
for j in range(len(str1)):
for i in range(len(str1)+1):
if str1[j:i] in str2 and len(str1[j:i])>len(longest):
longest = str1[j:i]
return longest
где одна строка содержит алфавиты в порядке, а другая содержит тестовую строку. Имейте в виду, что это O (n ^ 2) по сложности (не так важно для малых случаев).
Ответ 3
В алгоритмической перспективе наиболее оптимизированным подходом является использование дерева суффикса, чтобы найти самую длинную подстроку в данной строке. (Я реализовал оптимизированную версию дерева суффикса в python некоторое время назад. В случае, если вам интересно, вы можете проверить https://github.com/kasramvd/SuffixTree
В качестве другого хакерского способа вы можете использовать Numpy, чтобы найти самую большую подстроку, используя функции diff()
, where()
и split()
:
In [52]: s = 'pqsrjamabcdaskl'
In [54]: ''.join(max(np.split(list(s), np.where((np.diff([ord(i) for i in s]) == 1) == False)[0] + 1), key=len))
Out[54]: 'abcd'
Пояснение:
Логика этого кода заключается в том, чтобы найти индексы кастраторов, что разница их значения ascii равна 1.
В numpy мы можем просто сделать это с помощью функции np.diff
. Но поскольку для этого требуется массив элементов, мы можем использовать представление списка, чтобы передать список назначенных значений функции. Затем, сравнивая результат с 1, мы можем получить список массива bool следующим образом:
In [55]: np.diff([ord(i) for i in s]) == 1
Out[55]:
array([ True, False, False, False, False, False, False, True, True,
True, False, False, False, True], dtype=bool)
Теперь мы можем получить индексы элементов False, используя np.where
, чтобы передать их функции split
:
In [57]: np.split(list(s), np.where((np.diff([ord(i) for i in s]) == 1) == False)[0] + 1)
Out[57]:
[array(['p', 'q'],
dtype='<U1'), array(['s'],
dtype='<U1'), array(['r'],
dtype='<U1'), array(['j'],
dtype='<U1'), array(['a'],
dtype='<U1'), array(['m'],
dtype='<U1'), array(['a', 'b', 'c', 'd'],
dtype='<U1'), array(['a'],
dtype='<U1'), array(['s'],
dtype='<U1'), array(['k', 'l'],
dtype='<U1')]
+1
на самом деле потому, что np.split разделяет от 0 до нашего первого индекса, а затем от первого индекса до следующего и т.д.
И в конце мы можем получить самый длинный массив, используя функцию max
, передав len
в качестве ключевой функции.
Также обратите внимание, что этот подход даст вам самую длинную последовательную последовательность, если вы просто заботитесь о заказе, вы можете заменить == 1
на > 0
. Вот пример:
In [13]: s = 'pqsrjamabcdasklptvxz'
In [14]: ''.join(max(np.split(list(s), np.where((np.diff([ord(i) for i in s]) > 0) == False)[0] + 1), key=len))
Out[14]: 'klptvxz'
Ответ 4
другая версия, зацикленная на zip(strg, strg[1:])
, чтобы сравнить предыдущий и текущий символ в той же итерации цикла:
def longest_substring(strg):
substring = strg[0]
max_substring = ''
for cur, nxt in zip(strg, strg[1:]):
if ord(nxt) >= ord(cur):
substring += nxt
if len(substring) > len(max_substring):
max_substring = substring
else:
substring = nxt
return max_substring
сравнение символов с ord
таким образом имеет тот недостаток, что эти символы !"#$%&'()*+,-./0123456789:;=>[email protected][\]^_``abcdefghijklmnopqrstuvwxyz{|}~
будут считаться "в алфавитном порядке", вам может потребоваться настроить это для ваших нужд...
Ответ 5
Здесь простое, эффективное и (ИМО) вполне читаемое решение, которое уклоняется от того, что на самом деле означает "алфавитный порядок", взяв пользовательскую тестовую функцию в качестве параметра:
def longest_matching_substring(s, match):
current_run_length = 1
longest_run_length = 1
longest_run_end = 0
for i in range(1, len(s)):
if match(s[i-1], s[i]):
current_run_length += 1
else:
current_run_length = 1
if current_run_length > longest_run_length:
longest_run_length = current_run_length
longest_run_end = i
longest_run_start = longest_run_end - longest_run_length + 1
return s[longest_run_start:longest_run_end+1]
Вы можете использовать его, например. например:
print(longest_matching_substring('jamabcdaskl', lambda a,b: a < b))
который будет печатать "abcd
". Если вы хотите использовать сравнение без учета регистра и/или полностью игнорировать неалфавитные символы, вы можете сделать это, изменив тестовую функцию, например. например:
def is_alphabetized(a, b):
return a.lower() < b.lower()
# this prints "eSt"
print(longest_matching_substring('TeStInG', is_alphabetized))
Конечно, определив подходящую тестовую функцию, вы также можете использовать ту же функцию longest_matching_substring
, чтобы найти самую длинную последовательную подстроку, где смежные пары символов удовлетворяют любому произвольному критерию (например, "не содержит согласного а затем гласный" ). И вы можете использовать одну и ту же функцию для поиска наиболее долгого соответствия последовательных подпоследовательностей в других типах последовательностей, таких как списки и кортежи, а не только в строках.
(Эта реализация, однако, не работает для произвольных итерируемых типов: для обработки этих данных нам нужно будет запомнить текущую и самую длинную совпадающую подстроку, когда мы будем перебирать входные данные. Хотя это выполнимо, это несколько усложнит код, а также сделать его менее эффективным для обычных строк и других типов последовательностей.)
Ответ 6
Использование итерации:
import string
alphabet = string.ascii_lowercase
def check_alpha(sut):
iterate_alpha = iter(alphabet)
max_longest = ''
current_longest = ''
compare_against = next(iterate_alpha)
for l in sut:
if l == compare_against:
current_longest += l
compare_against = next(iterate_alpha, '')
else:
max_longest = max(current_longest, max_longest, key=len)
iterate_alpha = iter(alphabet)
current_longest = next((x for x in iterate_alpha if x == l), '')
compare_against = next(iterate_alpha, '')
return max(current_longest, max_longest, key=len)
In [39]: assert 'abcdefghi' == check_alpha('abcdezdflkjabcdefghiasldfjlkasdfjkaaabb')
In [40]: assert 'abcd' == check_alpha('jamabcdaskl')
In [83]: assert 'abcde' == check_alpha('aldksfjklasabcdedjfkladabcd')
In [89]: assert 'abcdefghijklmnopqrst' == check_alpha('adfadsabcdefghijklmnopqrst')
In [96]: assert 'ghijklmnopqrst' == check_alpha('adfadscghijklmnopqrst')
Ответ 7
Как насчет использования base string
альфа-символов и проверить, находится ли подстрока в этом base string
, а затем вернуть максимальную подстроку, найденную в базовой строке на основе ее длины?
Это пример:
def get_max_substring(a):
base = 'abcdefghijklmnopqrstuvwxyz'
possibilites = (a[k:k+i] for k in range(len(a)) for i in range(k, len(a)))
sub = (k for k in possibilites if k in base)
return max(sub, key= lambda x: len(x))
# Tests
a = 'jamabcdaskl'
final = get_max_substring(a)
print(final)
b = 'asdklfjalkdfjabcdefghijklmnopqrstuvwxyzaaabcasdkfjl;kasdf'
test = get_max_substring(b)
print(test)
Вывод:
abcd
abcdefghijklmnopqrstuvwxyz
Ответ 8
Здесь моя реализация:
import itertools
def pairwise(iterable):
# Taken from https://docs.python.org/3.6/library/itertools.html#itertools-recipes
a, b = itertools.tee(iterable)
next(b, None)
return zip(a, b)
def longest_sorted_substr(s):
# Split up in pairs
pairs = pairwise(s)
all_substrings = []
curr_substring = []
for i, pair in enumerate(pairs):
if ord(pair[0]) <= ord(pair[1]):
curr_substring.append(s[i])
else:
# Start a new substring
curr_substring.append(s[i])
all_substrings.append(''.join(curr_substring))
curr_substring = []
# Don't forget to add the last character and append the substring
curr_substring.append(s[-1])
all_substrings.append(''.join(curr_substring))
# Sort the substrings according to length and return the first one
all_substrings.sort(key=lambda x: len(x),
reverse=True)
return all_substrings[0]
Тесты (взяты из ответа @salparadise):
assert 'abcdefghi' == longest_sorted_substr('abcdezdflkjabcdefghiasldfjlkasdfjkaaabb')
assert 'abcd' == longest_sorted_substr('jamabcdaskl')
assert 'abcde' == longest_sorted_substr('aldksfjklasabcdedjfkladabcd')
assert 'abcdefghijklmnopqrst' == longest_sorted_substr('adfadsabcdefghijklmnopqrst')
assert 'cghijklmnopqrst' == longest_sorted_substr('adfadscghijklmnopqrst')
Я не сравнивал производительность с другими предлагаемыми ответами и помню, что он чувствителен к регистру и ожидает, что строка будет состоять из символов.
При необходимости вы можете сначала извлечь только буквы из строки и преобразовать ее в нижний регистр, используя:
import string
s = ''.join([letter for letter in s.lower() if letter in string.ascii_letters])
Ответ 9
Я немного подправил вам код и попробовал.
Кажется, он работает отлично.
Я все еще учащийся, не против моих незначительных ошибок, если они есть. Добавьте строку как s = '....'
x = 'abcdefghijklmnopqrstuvwxyz'
current_string = ''
count=0
longest_string = ''
for p in range(len(s)-1):
if x.index(s[p+1])-x.index(s[p]) < 0 and count == 0:
longest_string = s[count]
p +=1
elif x.index(s[p+1])-x.index(s[p]) < 0:
current_string = ''
elif x.index(s[p+1])-x.index(s[p]) >= 0:
current_string += s[p]
count += 1
if len (longest_string) < len (current_string + s[p+1]):
longest_string = current_string + s[p+1]
print('Longest substring in alphabetical order is:', longest_string)