Python 3:完美的字母顺序

时间:2017-06-10 07:17:08

标签: python string python-2.7 for-loop python-3.5

代码的目标是找到字符串中最长的字母子字符串。

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'的值来检查下一个字符,但这并不是'似乎什么都不做。

欢迎提供建议,提示,更正和严厉的批评。

编辑:看来我误导了你们中的一些人,所以我道歉了。我的意思是,如果我有一个字符串,它会按字母顺序提取最长的子字符串。在xyzbcdezzz的情况下,它将提取'bcdezzz',因为这是最长的字母顺序子字符串,而不是bcde。我当前代码的问题在于它给出了bccddeezzzzz。如果我可以在第一个条件为真时跳过一个循环,那么我认为它可能在我的代码中有效。

9 个答案:

答案 0 :(得分:11)

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))

对子程序的this post的信用。

修改

上述代码似乎并不适用于所有情况,因此我不得不重新设计该功能。

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开头的假设。这会迭代来自&#39; a&#39;&#39; ab&#39; ab&#39; abc&#39;,...的每个可能的子字符串,直到完整的字母串,然后存储最长的字符串检查中遇到的子字符串。

为了完整起见,这里的代码适用于任何最长的公共子字符串:

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)的复杂性(对于小的情况并不重要)。

答案 1 :(得分:8)

循环遍历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:;=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_``abcdefghijklmnopqrstuvwxyz{|}~将被视为“按字母顺序排列”。你可能需要根据自己的需要进行调整......

答案 2 :(得分:8)

从算法的角度来看,最优化的方法是使用后缀树来查找给定字符串中最长的子字符串。 (我之前在python中实现了后缀树的优化版本。如果您有兴趣,可以查看https://github.com/kasramvd/SuffixTree

作为另一种hacky方式,您可以使用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)

现在我们可以使用np.where获取False项的索引,将它们传递给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'

答案 3 :(得分:3)

这是一个简单,高效且(IMO)非常易读的解决方案,它通过将自定义测试功能作为参数来回避“字母顺序实际意味着什么”问题:

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函数来查找最长的连续子字符串,其中相邻的字符对满足任意条件(例如,“不包含”辅音后跟元音“)。您甚至可以使用相同的函数在列表和元组等其他序列类型中查找最长匹配的连续子序列,而不仅仅是字符串。

(但是,这个实现不适用于任意可迭代类型;为了处理这些,我们必须记住当前和最长匹配的子字符串,因为我们迭代输入。虽然可行,但这会使代码复杂化,并且使普通字符串和其他序列类型的效率降低。)

答案 4 :(得分:3)

编辑完成后,您的问题更加清晰。我尽可能少地修改了您的代码,以向您展示解决方案中的错误来自哪里。

以下是代码:

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]

如果添加有效,您将完全添加到当前字符串(最后一个字符current_string[-1]并且添加的崇拜者s[n]按顺序排列)

elif部分被简化为不检查s[n]s[n+1],因为它不反映我们要做的事情:我们不关心字符在整个字符串中是否按顺序排列{ {1}}我们关心当前的字符串(这个逻辑被上面的if语句捕获,否则只有在出现问题时才会被访问)

所以这里的变化是

s

elif s[n] > s[n+1]:
    if len(current_string) > len(longest_string) :

如有必要,添加新的获胜者并将当前字符串重置为不按顺序的字符

最后一组ifs检查else: if len(current_string) > len(longest_string): longest_string = current_string current_string = s[n] current_string的最后一个字符结束时的情况,虽然这是正确的,如果你在循环和打印后添加一个检查,它会更少分散注意力只有longest_string

s

这样输出是每种情况下第一个有效的最长字符串,而当其中一个字符串位于字符串的尾部时,输出不是2个不同的最长字符串

答案 5 :(得分:1)

使用迭代:

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')

答案 6 :(得分:1)

如何使用base string alphebetic字符并检查子字符串是否进入此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

答案 7 :(得分:1)

这是我的实施:

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])

答案 8 :(得分:0)

我稍微调了一下你的代码,然后试了一下。 它似乎工作得很好。 我仍然是一个学习者,不介意我的小错误,如果有的话。将您的字符串添加为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)