每次出现元音时反转字符串的函数?

function that reverses a string every time a vowel appears in it?

我正在尝试制作一个函数,对于字符串中每次出现的元音,反转所述字符串(并在执行此操作时包括元音)。就我的理解而言,该功能有些复杂,因此我需要一些帮助并可能对其进行细分。但是,我只想使用我目前正在学习的运算符和语句(for/while 和 if)。如果可能的话,我也想避免使用列表理解。

输入和输出应该是这样的:

示例输入是 reverse_per_vowel('aerith') 其中 returns 'iraeth'

如果我们把这个函数的过程分解成几个步骤,它应该是这样的:

(a)erith → (a)erith (第一个字母是元音,所以是相反。但是,因为它是字符串中的第一个字母,所以没有可见的变化。)

(ae)rith → (ea)rith (第二个字母也是元音,所以字符串中元音之前和包括元音的每个字母都被颠倒了。)

(eari)th → (irae)th (第四个字母是元音,所以导致并包括它的所有内容也被反转。请注意它如何解释字符串中先前被反转的字母。)

如您所见,字符串反转的次数是累积的,我不太确定如何对此进行编码。但是,我尝试编写该函数的一个组件。

我正在尝试什么

vowellist = 'aeiouAEIOU'
sampleword = 'aerith'

indexlist = []
for i in range(len(sampleword)):
    if sampleword[i] in vowel_list:
        indexlist.append(i)
indexlist

输出:[0, 1, 3]

此摘录不会反转字符串的任何部分,但它 returns 索引了字符串应反转的位置。我计划做的是以某种方式将这些索引插入到示例单词中,并使用 [::-1] 来反转字符串的一部分。但是,我不知道该怎么做,也不知道这是否是个好主意。任何帮助将不胜感激。

实现此目的的一种简单方法是使用递归:

vowels = set('aeiouAEIOU')

def reverse_per_vowel(s):
    if not s: # empty string
        return ''
    beforelast, last = s[:-1], s[-1]
    if last in vowels:
        return last + reverse_per_vowel(beforelast)[::-1]
    return reverse_per_vowel(beforelast) + last

print(reverse_per_vowel('aerith')) # iraeth

您可以通过简单修改 for 循环和一些高级切片来实现:

vowellist = 'aeiouAEIOU'
sampleword = 'aerith'

for i in range(len(sampleword)):
    if sampleword[i] in vowellist:
        sampleword = sampleword[i::-1] + sampleword[i + 1:]

print(sampleword)

每次迭代,如果元音出现,您可以重新分配新的部分反转的字符串。 输出:

iraeth

你可以帮助我的国家,检查my profile info

如果有很多元音,那么重复的反转似乎是可以避免的,因为第二次反转在某种程度上是对前一次反转的撤销。

是的,你可以使用这个算法:

  • 构建两个字符串。它们开始为空,并将其中第二个标记为“活动”。

  • 以相反的顺序访问输入字符:从最后到第一个

    • 只要是辅音就加到当前激活的字符串中
    • 当它是元音时,将活动字符串切换为另一个,并在那里添加元音
  • 在此过程结束时,反转第二个字符串和return两个字符串的连接:

VOWELS = set("aeiouAEIOU")

def reverse_per_vowel(s):
    endings = ["", ""]
    side = 1
    for c in reversed(s):
        if c in VOWELS:
            side = 1 - side  # Toggle between 0 and 1
        endings[side] += c
    return endings[0] + endings[1][::-1]

由于该算法不会在每次遇到元音时都反转,而是在最后只执行一次反转,因此它以线性时间复杂度运行,这与您按字面意义实现所描述的过程会得到的结果相反,它具有O(n²) 的最坏情况时间复杂度。

通过这种复杂性分析,我假设用字符扩展字符串是一个常数时间过程。如果对此有疑问,则用两个字符列表实现它,调用append并在过程结束时执行join以获得最终字符串:

VOWELS = set("aeiouAEIOU")

def reverse_per_vowel(s):
    endings = [[], []]
    side = 1
    for c in reversed(s):
        if c in VOWELS:
            side = 1 - side  # Toggle between 0 and 1
        endings[side].append(c)
    
    return "".join(endings[0] + endings[1][::-1])

使用列表使它变得非常简单和干净:

def reverse_per_vowel(word):
    result = []
    for letter in word:
        result.append(letter)
        if letter in 'aeiouAEIOU':
            result.reverse()
    return ''.join(result)

速度也很快。对于你的示例单词,它比目前发布的所有其他解决方案都快,对于一个有 1000 个字母的单词 ('aerith' * 167),只有 @trincot 的第二个解决方案快一点,其他的慢 2 到 10 倍.当然最终它真的输给了 trincot 的第二个解决方案,它在 10,000 个字母时快了大约 5 倍,在 100,000 个字母时快了大约 46 倍。

这是另一个 linear-time,比 trincot 的快一点(在包含多达一百万个字母的字符串上测试)。它将字母放入一个双端队列中,因此它可以有效地附加到左侧或右侧。并且它有一个标志,告诉当前结果是否是反向的。

from collections import deque

def reverse_per_vowel(word):
    result = deque()
    reverse = False
    for letter in word:
        if not reverse:
            result.append(letter)
        else:
            result.appendleft(letter)
        if letter in 'aeiouAEIOU':
            reverse = not reverse
    if reverse:
        result.reverse()
    return ''.join(result)