字符串中单词的相反顺序


17

任务

  • 系统会为您提供一个匹配的可变字符串[a-z]+( [a-z]+)*
  • 您必须将其变异为包含相同单词的字符串,但是顺序相反,以便“大家好”变成“大家好”。
  • 您不得使用固定数量以上的额外内存(因此,请勿将整个字符串或任何整个单词复制到刚分配的缓冲区中)。
  • 没有时间限制。绝望的低效率不会损害您的分数。
  • 如果您选择的语言不允许对字符串进行突变,则可以使用字符数组作为替代。

你的分数

  • 您的分数完全是根据您对字符串元素的分配次数来计算的(小分数最好)。如果您使用写入字符串的库函数,则其写入也将计数。
  • 假设输入s所需的分配数量为n(s)。然后,您的分数是所有输入s(与上面指定的正则表达式匹配)的n(s)/ length(s)的最大值(在学上,最高。如果无法精确计算,则可以使用可以证明的最低上限。
  • 如果可以证明您的算法使用的渐近分配较少,那么您可以平分秋色(即使得分相同,也可能发生这种情况,请参阅下文)。如果您不能这样做,则可以通过显示使用更少的额外内存来打破平局。但是,第一个抢七的条件始终优先。
  • 对于某些输入,每个字符都需要更改,因此得分不能低于1。
  • 我可以想到一个得分为2的简单算法(但我没有输入)。

关于至上和纽带的注意事项

  • 一组数字的最大值是不小于任何一个的最小数字。这与集合的最大值非常相似,除了{2/3,3/4,4/5,5/6,...}等一些无限集合没有单个最大元素,但仍然有一个最大值,在这种情况下1。
  • 如果您设法在我的得分2算法(例如)上仅“保存”固定数量的作业,您的得分仍将为2,因为输入越大,您将任意接近2。但是,如果涉及到决胜局,您将获胜。

1
如果这一切都归结为关于内存使用情况的破门得分2提交,我会有些伤心。我大多张贴了这个问题,不知道是否有人会设法得分小于2
本·米尔伍德

1
我没有正式的证明,但是我的直觉告诉我,使用恒定空间限制,不可能比2做得更好。如果仅对空间的变量声明进行计数,我可能会作弊并创建一个递归函数。但这只能little-omega(1)通过将其放在堆栈上来掩盖空间。
错误

2
@bacchusbeale是的,但是它会不断增加内存。
马丁·恩德2014年

4
如果严格执行规则,那么就不可能编写这样的程序。该字符串可以是任意长度,并且您至少需要某种存储索引的变量。因此,我只需要使字符串足够长即可超出变量的范围即可。要成功存储至少一个索引,所需的内存将随着字符串长度的增长而增加。
IchBinKeinBaum 2014年

3
@IchBinKeinBaum是正确的,不可能用O(1)额外的空间执行此任务。您需要O(log n)空间来存储索引位置,因为k位整数只能存储长度不超过的字符串2^k。限制字符串的长度使挑战变得毫无意义,因为每种算法都需要O(1)这种方式的空间。
丹尼斯

Answers:


4

Python,得分:2 1.5 1.25

这是直接的 primo的答案和我的答案组合。所以也归功于他!

证明仍在进行中,但是这里是要使用的代码!如果您发现分数大于1.25(或者存在错误)的反例,请告诉我!

当前最坏的情况是:

aa ... aa dcb ... cbd

其中每个字母正好有n个,分别是“ a”,“ b”,“ c”和“”(空格),并且正好是两个“ d”。字符串的长度为4n + 2,分配的数量为5n + 2,得分为5/4 = 1.25

该算法分为两个步骤:

  1. 找到k这样string[k]string[n-1-k]字界限
  2. 只需稍加修改就可以运行任何单词反转算法string[:k]+string[n-1-k:](即,第一个k和最后一个k字符的串联)。

n字符串的长度在哪里。

该算法提出改进来自于“小改”,在步骤2中它基本上是知识,在连接字符串,在位置的字符kk+1字界限(它们是手段,一个字的空间或第一/最后一个字符)这样我们就可以直接替换最终字符串中的位置字符kk+1相应的字符,从而节省一些分配。这从主机单词反转算法中消除了最坏的情况

在某些情况下,我们实际上找不到 k,在这种情况下,我们只对整个字符串运行“任何单词逆转算法”。

该代码很长,无法处理在“串联”字符串上运行单词反转算法的这四种情况:

  1. 何时k未找到(f_long = -2
  2. string[k] != ' ' and string[n-1-k] != ' 'f_long = 0
  3. string[k] != ' ' and string[n-1-k] == ' 'f_long = 1
  4. string[k] == ' ' and string[n-1-k] != ' 'f_long = -1

我确定代码可以缩短。当前很长一段时间是因为一开始我对整个算法不了解。我确定可以将其设计为用较短的代码表示)

样品运行(第一个是我的,第二个是primo的):

输入字符串:bc def ghij
“ ghij def bc a”:9、13、0.692
“ ghij def bc a”:9、13、0.692
输入字符串:ab cdefghijklmnopqrstuvw xyz
“ zyxwvutsrqponmlkjihgf edc ab”:50、50、1.000
“ zyxwvutsrqponmlkjihgf edc ab”:51、50、1.020
输入字符串:abcdefg hijklmnopqrstuvwx
“ hijklmnopqrstuvwx gfedcb a”:38、31、1.226
“ hijklmnopqrstuvwx gfedcb a”:38、31、1.226
输入字符串:a bc de fg hi jk lm no pq rs tu vw xy zc
“ zc xy vw turs pq no lm jk hi fg de bc a”:46、40、1.150
“ zc xy vw turs pq no lm jk hi fg de bc a”:53、40、1.325
输入字符串:aaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaa dcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbd
“dcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbd aaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaa一个”:502,402,1.249
“dcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbd aaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaa一个”:502,402,1.249

您可以看到分数几乎相同,除了第三个示例中主机单词反转算法的最坏情况(我的方法得出的分数小于1.25)

DEBUG = False

def find_new_idx(string, pos, char, f_start, f_end, b_start, b_end, f_long):
    if DEBUG: print 'Finding new idx for s[%d] (%s)' % (pos, char)
    if f_long == 0:
        f_limit = f_end-1
        b_limit = b_start
    elif f_long == 1:
        f_limit = f_end-1
        b_limit = b_start+1
    elif f_long == -1:
        f_limit = f_end-2
        b_limit = b_start
    elif f_long == -2:
        f_limit = f_end
        b_limit = b_start

    if (f_start <= pos < f_limit or b_limit < pos < b_end) and char == ' ':
        word_start = pos
        word_end = pos+1
    else:
        if pos < f_limit+1:
            word_start = f_start
            if DEBUG: print 'Assigned word_start from f_start (%d)' % f_start
        elif pos == f_limit+1:
            word_start = f_limit+1
            if DEBUG: print 'Assigned word_start from f_limit+1 (%d)' % (f_limit+1)
        elif b_limit <= pos:
            word_start = b_limit
            if DEBUG: print 'Assigned word_start from b_limit (%d)' % b_limit
        elif b_limit-1 == pos:
            word_start = b_limit-1
            if DEBUG: print 'Assigned word_start from b_limit-1 (%d)' % (b_limit-1)
        i = pos
        while f_start <= i <= f_limit or 0 < b_limit <= i < b_end:
            if i==f_limit or i==b_limit:
                cur_char = 'a'
            elif i!=pos:
                cur_char = string[i]
            else:
                cur_char = char
            if cur_char == ' ':
                word_start = i+1
                if DEBUG: print 'Assigned word_start from loop'
                break
            i -= 1

        if b_limit <= pos:
            word_end = b_end
            if DEBUG: print 'Assigned word_end from b_end (%d)' % b_end
        elif b_limit-1 == pos:
            word_end = b_limit
            if DEBUG: print 'Assigned word_end from b_limit (%d)' % (b_limit)
        elif pos < f_limit+1:
            word_end = f_limit+1
            if DEBUG: print 'Assigned word_end from f_limit+1 (%d)' % (f_limit+1)
        elif pos == f_limit+1:
            word_end = f_limit+2
            if DEBUG: print 'Assigned word_end from f_limit+2 (%d)' % (f_limit+2)
        i = pos
        while f_start <= i <= f_limit or 0 < b_limit <= i < b_end:
            if i==f_limit or i==b_limit:
                cur_char = 'a'
            elif i!=pos:
                cur_char = string[i]
            else:
                cur_char = char
            if cur_char == ' ':
                word_end = i
                if DEBUG: print 'Assigned word_end from loop'
                break
            i += 1
    if DEBUG: print 'start, end: %d, %d' % (word_start, word_end)
    word_len = word_end - word_start
    offset = word_start-f_start
    result = (b_end-offset-(word_end-pos)) % b_end
    if string[result] == ' ' and (b_start == -1 or result not in {f_end-1, b_start}):
        return len(string)-1-result
    else:
        return result

def process_loop(string, start_idx, f_start, f_end, b_start, b_end=-1, f_long=-2, dry_run=False):
    assignments = 0
    pos = start_idx
    tmp = string[pos]
    processed_something = False
    count = 0
    while pos != start_idx or not processed_something:
        count += 1
        if DEBUG and count > 20:
            print '>>>>>Break!<<<<<'
            break
        new_pos = find_new_idx(string, pos, tmp, f_start, f_end, b_start, b_end, f_long)
        if DEBUG:
            if dry_run:
                print 'Test:',
            else:
                print '\t',
            print 'New idx for s[%d] (%s): %d (%s)' % (pos, tmp, new_pos, string[new_pos])
        if dry_run:
            tmp = string[new_pos]
            if new_pos == dry_run:
                return True
        elif pos == new_pos:
            break
        elif tmp == string[new_pos]:
            pass
        else:
            tmp, string[new_pos] = string[new_pos], tmp
            assignments += 1
        pos = new_pos
        processed_something = True
    if dry_run:
        return False
    return assignments

def reverse(string, f_start, f_end, b_start, b_end=-1, f_long=-2):
    if DEBUG: print 'reverse: %d %d %d %d %d' % (f_start, f_end, b_start, b_end, f_long)
    if DEBUG: print
    if DEBUG: print ''.join(string)
    assignments = 0
    n = len(string)
    if b_start == -1:
        for i in range(f_start, f_end):
            if string[i] == ' ':
                continue
            if DEBUG: print 'Starting from i=%d' % i
            if any(process_loop(string, j, f_start, f_end, -1, f_end, dry_run=i) for j in range(f_start, i) if string[j] != ' '):
                continue
            if DEBUG:
                print
                print 'Finished test'
            assignments += process_loop(string, i, f_start, f_end, -1, f_end)
            if DEBUG: print
            if DEBUG: print ''.join(string)
        for i in range(f_start, (f_start+f_end-1)/2):
            if (string[i] == ' ' and string[n-1-i] != ' ') or (string[i] != ' ' and string[n-1-i] == ' '):
                string[i], string[n-1-i] = string[n-1-i], string[i]
                assignments += 2
    else:
        for i in range(f_start, f_end)+range(b_start, b_end):
            if string[i] == ' ' and i not in {f_end-1, b_start}:
                continue
            if DEBUG: print 'Starting from i=%d' % i
            if any(process_loop(string, j, f_start, f_end, b_start, b_end, f_long, i) for j in range(f_start, f_end)+range(b_start, b_end) if j<i and (string[j] != ' ' or j in {f_end-1, b_start})):
                continue
            assignments += process_loop(string, i, f_start, f_end, b_start, b_end, f_long)
            if DEBUG: print
            if DEBUG: print ''.join(string)
        for i in range(f_start, f_end-1):
            if (string[i] == ' ' and string[n-1-i] != ' ') or (string[i] != ' ' and string[n-1-i] == ' '):
                string[i], string[n-1-i] = string[n-1-i], string[i]
                assignments += 2
    return assignments

class SuperList(list):
    def index(self, value, start_idx=0):
        try:
            return self[:].index(value, start_idx)
        except ValueError:
            return -1

    def rindex(self, value, end_idx=-1):
        end_idx = end_idx % (len(self)+1)
        try:
            result = end_idx - self[end_idx-1::-1].index(value) - 1
        except ValueError:
            return -1
        return result

def min_reverse(string):
    assignments = 0
    lower = 0
    upper = len(string)
    while lower < upper:
        front = string.index(' ', lower) % (upper+1)
        back = string.rindex(' ', upper)
        while abs(front-lower - (upper-1-back)) > 1 and front < back:
            if front-lower < (upper-1-back):
                front = string.index(' ', front+1) % (upper+1)
            else:
                back = string.rindex(' ', back)
            if DEBUG: print lower, front, back, upper
        if front > back:
            break
        if DEBUG: print lower, front, back, upper
        if abs(front-lower - (upper-1-back)) > 1:
            assignments += reverse(string, lower, upper, -1)
            lower = upper
        elif front-lower < (upper-1-back):
            assignments += reverse(string, lower, front+1, back+1, upper, -1)
            lower = front+1
            upper = back+1
        elif front-lower > (upper-1-back):
            assignments += reverse(string, lower, front, back, upper, 1)
            lower = front
            upper = back
        else:
            assignments += reverse(string, lower, front, back+1, upper, 0)
            lower = front+1
            upper = back
    return assignments

def minier_find_new_idx(string, pos, char):
    n = len(string)
    try:
        word_start = pos - next(i for i, char in enumerate(string[pos::-1]) if char == ' ') + 1
    except:
        word_start = 0
    try:
        word_end = pos + next(i for i, char in enumerate(string[pos:]) if char == ' ')
    except:
        word_end = n
    word_len = word_end - word_start
    offset = word_start
    result = (n-offset-(word_end-pos))%n
    if string[result] == ' ':
        return n-result-1
    else:
        return result

def minier_process_loop(string, start_idx, dry_run=False):
    assignments = 0
    pos = start_idx
    tmp = string[pos]
    processed_something = False
    while pos != start_idx or not processed_something:
        new_pos = minier_find_new_idx(string, pos, tmp)
        #print 'New idx for s[%d] (%s): %d (%s)' % (pos, tmp, new_pos, string[new_pos])
        if pos == new_pos:
            break
        elif dry_run:
            tmp = string[new_pos]
            if new_pos == dry_run:
                return True
        elif tmp == string[new_pos]:
            pass
        else:
            tmp, string[new_pos] = string[new_pos], tmp
            assignments += 1
        pos = new_pos
        processed_something = True
    if dry_run:
        return False
    return assignments

def minier_reverse(string):
    assignments = 0
    for i in range(len(string)):
        if string[i] == ' ':
            continue
        if any(minier_process_loop(string, j, dry_run=i) for j in range(i) if string[j] != ' '):
            continue
        assignments += minier_process_loop(string, i)
    n = len(string)
    for i in range(n/2):
        if string[i] == ' ' and string[n-i-1] != ' ':
            string[i], string[n-i-1] = string[n-i-1], string[i]
            assignments += 2
        elif string[n-i-1] == ' ' and string[i] != ' ':
            string[i], string[n-i-1] = string[n-i-1], string[i]
            assignments += 2
    return assignments

def main():
    while True:
        str_input = raw_input('Enter string: ')
        string = SuperList(str_input)
        result = min_reverse(string)
        n = len(string)
        print '"%s": %d, %d, %.3f' % (''.join(string), result, n, 1.0*result/n)
        string = SuperList(str_input)
        result2 = minier_reverse(string)
        print '"%s": %d, %d, %.3f' % (''.join(string), result2, n, 1.0*result2/n)

if __name__ == '__main__':
    main()

Python,得分:1.5

确切的分配数量可以通过以下公式估算:

n <= 1.5 *长度(字符串)

最坏的情况是:

abcdefghi jklmnopqrstuvwxyzzz

长度为37的字符串上有55个作业。

这个想法与我先前的想法相似,只是在此版本中,我尝试在字边界处查找前缀和后缀,且其长度差最大为1。然后,我在该前缀和后缀上运行我先前的算法(想象它们是串联在一起的) 。然后继续处理未处理的零件。

例如,对于先前最坏的情况:

ab | ab | C

我们将首先对“ ab”和“ c”(4个作业)进行单词反转:

c | ab | ab

我们知道在边界处它曾经是空间(有很多情况需要处理,但是您可以做到),因此我们不需要在边界处对空间进行编码,这是对先前算法的主要改进。

然后最后我们运行中间的四个字符来获得:

cba抗体

总共8个分配,这是这​​种情况下的最佳选择,因为所有8个字符均已更改。

由于消除了先前算法中的最坏情况,因此消除了先前算法中的最坏情况。

查看一些示例运行(并与@primo的答案进行比较-他是第二行):

输入字符串:我可以做任何事情
“我可以做的任何事”:20、17
“我可以做的任何事”:17、17
输入字符串:abcdef ghijklmnopqrs
“ ghijklmnopqrs fedcb a”:37,25
“ ghijklmnopqrs fedcb a”:31、25
输入字符串:abcdef ghijklmnopqrst
“ ghijklmnopqrst fedcb a”:38、26
“ ghijklmnopqrst fedcb a”:32、26
输入字符串:abcdefghi jklmnozzzzzzzzzzzzzzzzzzz
“ jklmnozzzzzzzzzzzzzzzzzzzzz ihgfedcb a”:59,41
“ jklmnozzzzzzzzzzzzzzzzzzz ihgfedcb a”:45、41
输入字符串:abcdefghi jklmnopqrstuvwxyzzz
“ jklmnopqrstuvwxyzzz ihgfedcb a”:55、37
“ jklmnopqrstuvwxyzzz ihgfedcb a”:45、37
输入字符串:ab ababababababac
“ cababababababa ab”:30、30
“ cababababababa ab”:31、30
输入字符串:ab abababababababcabc
“ cbababababababa ab”:32、32
“ cbababababababa ab”:33、32
输入字符串:abc d abc
“ abc d abc”:0、9
“ abc d abc”:0、9
输入字符串:abc dca
“ acd abc”:6、9
“ acd abc”:4、9
输入字符串:abc ababababababc
“ cbabababababa abc”:7、29
“ cbabababababa abc”:5、29

primo的答案通常更好,尽管在某些情况下我可以拥有1点优势=)

而且他的代码比我的代码短得多,哈哈。

DEBUG = False

def find_new_idx(string, pos, char, f_start, f_end, b_start, b_end, f_long):
    if DEBUG: print 'Finding new idx for s[%d] (%s)' % (pos, char)
    if f_long == 0:
        f_limit = f_end-1
        b_limit = b_start
    elif f_long == 1:
        f_limit = f_end-1
        b_limit = b_start+1
    elif f_long == -1:
        f_limit = f_end-2
        b_limit = b_start
    elif f_long == -2:
        f_limit = f_end
        b_limit = b_start

    if (f_start <= pos < f_limit or b_limit < pos < b_end) and (char == ' ' or char.isupper()):
        word_start = pos
        word_end = pos+1
    else:
        if pos < f_limit+1:
            word_start = f_start
            if DEBUG: print 'Assigned word_start from f_start (%d)' % f_start
        elif pos == f_limit+1:
            word_start = f_limit+1
            if DEBUG: print 'Assigned word_start from f_limit+1 (%d)' % (f_limit+1)
        elif b_limit <= pos:
            word_start = b_limit
            if DEBUG: print 'Assigned word_start from b_limit (%d)' % b_limit
        elif b_limit-1 == pos:
            word_start = b_limit-1
            if DEBUG: print 'Assigned word_start from b_limit-1 (%d)' % (b_limit-1)
        i = pos
        if not (i < f_limit and b_limit < i):
            i -= 1
        while f_start <= i < f_limit or 0 < b_limit < i < b_end:
            if i!=pos:
                cur_char = string[i]
            else:
                cur_char = char
            if cur_char == ' ' or cur_char.isupper():
                word_start = i+1
                if DEBUG: print 'Assigned word_start from loop'
                break
            i -= 1

        if b_limit <= pos:
            word_end = b_end
            if DEBUG: print 'Assigned word_end from b_end (%d)' % b_end
        elif b_limit-1 == pos:
            word_end = b_limit
            if DEBUG: print 'Assigned word_end from b_limit (%d)' % (b_limit)
        elif pos < f_limit+1:
            word_end = f_limit+1
            if DEBUG: print 'Assigned word_end from f_limit+1 (%d)' % (f_limit+1)
        elif pos == f_limit+1:
            word_end = f_limit+2
            if DEBUG: print 'Assigned word_end from f_limit+2 (%d)' % (f_limit+2)
        i = pos
        if not (i < f_limit and b_limit < i):
            i += 1
        while f_start <= i < f_limit or 0 < b_limit < i < b_end:
            if i!=pos:
                cur_char = string[i]
            else:
                cur_char = char
            if cur_char == ' ' or cur_char.isupper():
                word_end = i
                if DEBUG: print 'Assigned word_end from loop'
                break
            i += 1
    if DEBUG: print 'start, end: %d, %d' % (word_start, word_end)
    word_len = word_end - word_start
    offset = word_start-f_start
    return (b_end-offset-(word_end-pos)) % b_end

def process_loop(string, start_idx, f_start, f_end, b_start, b_end=-1, f_long=-2, dry_run=False):
    assignments = 0
    pos = start_idx
    tmp = string[pos]
    processed_something = False
    count = 0
    while pos != start_idx or not processed_something:
        count += 1
        if count > 20:
            if DEBUG: print 'Break!'
            break
        new_pos = find_new_idx(string, pos, tmp, f_start, f_end, b_start, b_end, f_long)
        #if dry_run:
        #    if DEBUG: print 'Test:',
        if DEBUG: print 'New idx for s[%d] (%s): %d (%s)' % (pos, tmp, new_pos, string[new_pos])
        if pos == new_pos:
            break
        elif dry_run:
            tmp = string[new_pos]
            if new_pos == dry_run:
                return True
        elif tmp == string[new_pos]:
            pass
        elif tmp == ' ':
            if b_start!=-1 and new_pos in {f_end-1, b_start}:
                tmp, string[new_pos] = string[new_pos], tmp
            else:
                tmp, string[new_pos] = string[new_pos], '@'
            assignments += 1
        elif string[new_pos] == ' ':
            if b_start!=-1 and new_pos in {f_end-1, b_start}:
                tmp, string[new_pos] = string[new_pos], tmp
            else:
                tmp, string[new_pos] = string[new_pos], tmp.upper()
            assignments += 1
        else:
            tmp, string[new_pos] = string[new_pos], tmp
            assignments += 1
        pos = new_pos
        processed_something = True
    if dry_run:
        return False
    return assignments

def reverse(string, f_start, f_end, b_start, b_end=-1, f_long=-2):
    if DEBUG: print 'reverse: %d %d %d %d %d' % (f_start, f_end, b_start, b_end, f_long)
    if DEBUG: print
    if DEBUG: print ''.join(string)
    assignments = 0
    if b_start == -1:
        for i in range(f_start, (f_start+f_end)/2):
            if DEBUG: print 'Starting from i=%d' % i
            if any(process_loop(string, j, f_start, f_end, -1, f_end, dry_run=i) for j in range(f_start, i)):
                continue
            assignments += process_loop(string, i, f_start, f_end, -1, f_end)
            if DEBUG: print
            if DEBUG: print ''.join(string)
    else:
        for i in range(f_start, f_end):
            if DEBUG: print 'Starting from i=%d' % i
            if any(process_loop(string, j, f_start, f_end, b_start, b_end, f_long, i) for j in range(f_start, i)):
                continue
            assignments += process_loop(string, i, f_start, f_end, b_start, b_end, f_long)
            if DEBUG: print
            if DEBUG: print ''.join(string)
    for i in range(len(string)):
        if string[i] == '@':
            string[i] = ' '
            assignments += 1
        if string[i].isupper():
            string[i] = string[i].lower()
            assignments += 1
    return assignments

class SuperList(list):
    def index(self, value, start_idx=0):
        try:
            return self[:].index(value, start_idx)
        except ValueError:
            return -1

    def rindex(self, value, end_idx=-1):
        end_idx = end_idx % (len(self)+1)
        try:
            result = end_idx - self[end_idx-1::-1].index(value) - 1
        except ValueError:
            return -1
        return result

def min_reverse(string):
    # My algorithm
    assignments = 0
    lower = 0
    upper = len(string)
    while lower < upper:
        front = string.index(' ', lower) % (upper+1)
        back = string.rindex(' ', upper)
        while abs(front-lower - (upper-1-back)) > 1 and front < back:
            if front-lower < (upper-1-back):
                front = string.index(' ', front+1) % (upper+1)
            else:
                back = string.rindex(' ', back)
            if DEBUG: print lower, front, back, upper
        if front > back:
            break
        if DEBUG: print lower, front, back, upper
        if abs(front-lower - (upper-1-back)) > 1:
            assignments += reverse(string, lower, upper, -1)
            lower = upper
        elif front-lower < (upper-1-back):
            assignments += reverse(string, lower, front+1, back+1, upper, -1)
            lower = front+1
            upper = back+1
        elif front-lower > (upper-1-back):
            assignments += reverse(string, lower, front, back, upper, 1)
            lower = front
            upper = back
        else:
            assignments += reverse(string, lower, front, back+1, upper, 0)
            lower = front+1
            upper = back
    return assignments

def minier_find_new_idx(string, pos, char):
    n = len(string)
    try:
        word_start = pos - next(i for i, char in enumerate(string[pos::-1]) if char == ' ') + 1
    except:
        word_start = 0
    try:
        word_end = pos + next(i for i, char in enumerate(string[pos:]) if char == ' ')
    except:
        word_end = n
    word_len = word_end - word_start
    offset = word_start
    result = (n-offset-(word_end-pos))%n
    if string[result] == ' ':
        return n-result-1
    else:
        return result

def minier_process_loop(string, start_idx, dry_run=False):
    assignments = 0
    pos = start_idx
    tmp = string[pos]
    processed_something = False
    while pos != start_idx or not processed_something:
        new_pos = minier_find_new_idx(string, pos, tmp)
        #print 'New idx for s[%d] (%s): %d (%s)' % (pos, tmp, new_pos, string[new_pos])
        if pos == new_pos:
            break
        elif dry_run:
            tmp = string[new_pos]
            if new_pos == dry_run:
                return True
        elif tmp == string[new_pos]:
            pass
        else:
            tmp, string[new_pos] = string[new_pos], tmp
            assignments += 1
        pos = new_pos
        processed_something = True
    if dry_run:
        return False
    return assignments

def minier_reverse(string):
    # primo's answer for comparison
    assignments = 0
    for i in range(len(string)):
        if string[i] == ' ':
            continue
        if any(minier_process_loop(string, j, dry_run=i) for j in range(i) if string[j] != ' '):
            continue
        assignments += minier_process_loop(string, i)
    n = len(string)
    for i in range(n/2):
        if string[i] == ' ' and string[n-i-1] != ' ':
            string[i], string[n-i-1] = string[n-i-1], string[i]
            assignments += 2
        elif string[n-i-1] == ' ' and string[i] != ' ':
            string[i], string[n-i-1] = string[n-i-1], string[i]
            assignments += 2
    return assignments

def main():
    while True:
        str_input = raw_input('Enter string: ')
        string = SuperList(str_input)
        result = min_reverse(string)
        print '"%s": %d, %d' % (''.join(string), result, len(string))
        string = SuperList(str_input)
        result2 = minier_reverse(string)
        print '"%s": %d, %d' % (''.join(string), result2, len(string))

if __name__ == '__main__':
    main()

Python,分数:渐近2,通常情况下少得多

由于空间限制,删除了旧代码

想法是遍历每个索引,并且对于每个索引i,我们采用字符,计算新位置j,存储位置的字符j,将分配ij,然后在索引处重复字符j。由于我们需要空间信息来计算新位置,因此我将旧空间编码为新字母的大写版本,并将新空间编码为“ @”。


如果可以将最坏情况下的单词数减少为字符串的长度(例如,以length(string)/3某种方式强行将最坏情况下的每个单词的长度至少设为2),那么分数将小于2(在上面的示例中为1.67)
Justhalf

1
我给我添加了一个交换计数器;实际上,您的确在最坏的情况下击败了我的(但在一般情况下却没有)。需要找到一种解决方法;)
primo 2014年

127行:if any(process_loop(...) for j in range(...))是否不需要计算这些过程循环中的分配?
primo

那不做任何分配。如果看到的话,dry_run参数设置为非零(值为i)。在内process_loop,如果dry_run非零,则不会进行任何赋值。
2014年

1
我想我现在的情况更好了。本质上,将具有不同最坏情况行为的两种不同方法组合在一起,以消除这两种情况的最坏情况。我喜欢。我认为一般来说,这可能是最好的方法,尽管似乎可以将两种(或更多)完全不同的方法结合起来,以进一步减少最坏的情况。
2014年

14

Perl,得分1.3̅

对于每个非空格字符,执行一次分配,对于每个空格字符,执行两次分配。因为空格字符不能超过字符总数的一半,所以最差情况的得分是 1.5

算法没有改变,但是我可以证明一个较低的上限。让我们做两个观察:

  1. 与空间直接相对的空间无需交换。
  2. 与空格直接相对的单个字符单词在主阶段不会交换,而在结尾阶段只会交换一次。

然后可以看出,渐近1/2个空格的理论“最坏情况”根本不是最坏情况: ab c d e f g h i ...

$ echo ab c d e f g h i j k l m n o p q r s t u v w x y z|perl reverse-inplace.pl
z y x w v u t s r q p o n m l k j i h g f e d c ab
swaps: 51; len: 50
ratio: 1.02

实际上,这是一个很好的案例。

为了防止在上面观察到一个和两个,需要将每个一个字符的单词重新定位到三个或更多字符长的单词中间。这将表明最坏的情况是渐近地包含1/3个空格:a bcd a bcd a ... bc

$ echo a bcd a bcd a bcd a bcd a bcd a bc|perl reverse-inplace.pl
bc a bcd a bcd a bcd a bcd a bcd a
swaps: 45; len: 34
ratio: 1.32352941176471

或等效地,只有两个字符的单词: a bc de fg hi jk ...

$ echo a bc de fg hi jk lm no pq rs tu vx xy|perl reverse-inplace.pl
xy vx tu rs pq no lm jk hi fg de bc a
swaps: 49; len: 37
ratio: 1.32432432432432

由于最坏情况渐近地包含1/3个空格,因此最坏情况得分为1.3̅

#!perl -l
use warnings;

$words = <>;
chomp($words);
$len = length($words);
$words .= ' ';
$spaces = 0;
# iterate over the string, count the spaces
$spaces++ while $words =~ m/ /g;

$origin = 0;
$o = vec($words, $origin, 8);
$cycle_begin = $origin;
$swaps = 0;

# this possibly terinates one iteration early,
# if the last char is a one-cycle (i.e. moves to its current location)
# one-cycles previous to the last are iterated, but not swapped.
while ($i++ < $len - $spaces || !$was_cycle) {
  $w_start = rindex($words, ' ', $origin);
  $w_end = index($words, ' ', $origin);
  $pos = ($origin - $w_start) - 1;
  $target = $len - ($w_end - $pos);
  $t = vec($words, $target, 8);

  if ($t == 32) {
    $target = $len - $target - 1;
    $t = vec($words, $target, 8);
  }

  # char is already correct, possibly a one-cycle
  if ($t != $o) {
    $swaps += 1;
    vec($words, $target, 8) = $o;
  }

  $origin = $target;
  $o = $t;
  if ($origin == $cycle_begin) {
    if ($i < $len - $spaces) {
      # backtrack through everything we've done up to this point
      # to find the next unswapped char ...seriously.
      $origin += 1;
      if (vec($words, $origin, 8) == 32) {
        $origin += 1;
      }
      $bt_origin = 0;
      $bt_cycle_begin = 0;
      while ($bt_cycle_begin < $origin) {
        $w_start = rindex($words, ' ', $bt_origin);
        $w_end = index($words, ' ', $bt_origin);
        $pos = ($bt_origin - $w_start) - 1;
        $target = $len - ($w_end - $pos);
        $t = vec($words, $target, 8);

        if ($t == 32) {
          $target = $len - $target - 1;
          $t = vec($words, $target, 8);
        }

        if ($target == $bt_cycle_begin) {
          $bt_origin = ++$bt_cycle_begin;
          if (vec($words, $bt_origin, 8) == 32) {
            $bt_origin = ++$bt_cycle_begin;
          }
        } else {
          $bt_origin = $target;
        }

        if ($target == $origin) {
          $origin += 1;
          if (vec($words, $origin, 8) == 32) {
            $origin += 1;
          }
          $bt_origin = $bt_cycle_begin = 0;
        }
      }
    }

    $cycle_begin = $origin;
    $o = vec($words, $origin, 8);
    $was_cycle = 1;
  } else {
    $was_cycle = 0;
  }
}

for $i (0..$len/2-1) {
  $mirror = $len - $i - 1;
  $o = vec($words, $i, 8);
  $m = vec($words, $mirror, 8);
  # if exactly one is a space...
  if (($o == 32) ^ ($m == 32)) {
    $swaps += 2;
    vec($words, $mirror, 8) = $o;
    vec($words, $i, 8) = $m;
  }
}

chop($words);
print $words;
print "swaps: $swaps; len: $len";
print 'ratio: ', $swaps/$len;

编辑:添加了交换计数器和比率。

输入来自标准输入。用法示例:

$ echo where in the world is carmen sandiego|perl reverse-inplace.pl
sandiego carmen is world the in where
swaps: 35; len: 37
ratio: 0.945945945945946

方法

首先,将字符串的第一个字符移至其最终目的地。然后将刚刚替换的字符移动到其目的地,依此类推。直到满足以下两个条件之一为止,该过程将继续:

  1. 字符应换成空格。
    发生这种情况时,角色不会与空格交换,而是交换到空格的镜像位置。该算法从该位置继续。
  2. 已经达到一个周期。
    当目标返回到当前循环的初始起始位置时,需要找到下一个未交换的字符(或者,任何未交换的字符都可以)。为此,在恒定的内存约束下,将回溯到此为止已进行的所有交换。

在此阶段之后,每个非空格字符最多只能移动一次。最后,所有空格字符都将在其镜像位置交换为字符,每个空格需要进行两次赋值操作。


哇,太酷了。您能解释为什么将角色放在空间的镜像位置会给出正确答案吗?
Justhalf 2014年

1
@Niklas,我认为不可能。因为要进行回溯,所以您需要空间位置信息。如果您覆盖该信息,则无法进行回溯。
2014年

1
我在此处的回答中与算法进行了比较:codegolf.stackexchange.com/a/26952/16337
justhalf 2014年

1
@justhalf在最后一个字符串中,所有空格都将处于其镜像位置。因此,我们可以安全地使用此位置来存储替换空格的字符,并在最后切换它们。
普里莫

1
做得好。我有一个类似的想法,但没有想到只是将这些空间留在原处然后进行镜像。
IchBinKeinBaum 2014年

7

露比2分

首先,一个非常基本的算法。它首先反转整个字符串,然后再次反转字符串中的每个单词。在最坏的情况下(一个单词,偶数个字符),得分变为​​2。

def revstring(s, a, b)
  while a<b
    h = s[a]
    s[a] = s[b]
    s[b] = h
    a += 1
    b -= 1
  end
  s
end

def revwords(s)
  revstring(s, 0, s.length-1)
  a = 0
  while a<s.length
    b = a+1
    b += 1 while b<s.length and s[b]!=" "
    revstring(s, a, b-1)
    a = b+1
  end
  s
end

用法:

> revwords("hello there everyone")
"everyone there hello"

为什么不使用Ruby内置函数来反转字符串?会改变分数吗?
AL

使用,s [a],s [b] = s [b],s [a]
Thaha kp

5

C ++:得分2

#include<iostream>
#include<algorithm>

void rev(std::string& s)
{
    std::reverse(s.begin(),s.end());
    std::string::iterator i=s.begin(),j=s.begin();
    while(i!=s.end())
    {
        while(i!=s.end()&&(*i)==' ')
            i++;
        j=i;
        while(i!=s.end()&&(*i)!=' ')
            i++;
        std::reverse(j,i);
    }
}

int main()
{
    std::string s;
    getline(std::cin,s);
    rev(s);
    std::cout<<s;
}

2
我测试了 效果很好!
bacchusbeale 2014年

2

Rebol

reverse-words: function [
    "Reverse the order of words. Modifies and returns string (series)"
    series [string!] "At position (modified)"
  ][
    first-time: on
    until [
        reverse/part series f: any [
            if first-time [tail series]
            find series space
            tail series
        ]
        unless first-time [series: next f]
        first-time: off
        tail? series
    ]

    series: head series
]

我尚不清楚该评分。此代码中没有直接的字符串分配。一切都由一个人处理reverse/part在字符串内并在整个字符串上进行就地反转。

有关代码的一些细节:

  • 遍历字符串(series),直到到达tail?

  • 第一次循环完全反转字符串- reverse/part series tail series(与相同reverse series

  • 然后反转在进一步迭代中找到的每个单词- reverse/part series find series space

  • 找到精疲力尽的单词后返回 tail series以便将字符串中的最后一个单词反转-reverse/part series tail series

Rebol允许通过内部指针遍历字符串。您可以在以下位置看到它series: next f(将指针移动到空格后,因此移动下一个单词),然后series: head series(将指针复位到头部)。

看到 系列更多信息,。

Rebol控制台中的用法示例:

>> reverse-words "everyone there hello"
== "hello there everyone"

>> x: "world hello"
== "world hello"

>> reverse-words x
== "hello world"

>> x
== "hello world"

>> reverse-words "hello"
== "hello"

在第一遍,每个字符都重新定位一次,在第二遍,每个非空格字符都重新定位。对于具有相对较少的空间任意大的输入,将比分逼近2
普里莫

2

C:得分2

这只会翻转整个字符串一次,然后反转每个单词。

#include <stdio.h>
#include <string.h>

void reverse(char *s,unsigned n){
    char c;
    unsigned i=0,r=1;
    while(i < n){ //no swap function in C 
        c=s[i];
        s[i++]=s[n];
        s[n--]=c;
    }
}

unsigned wordlen(char *s){
    unsigned i=0;
    while (s[i] != ' ' && s[i]) ++i;
    return i;
}

int main(int argc, char **argv) {
    char buf[]="reverse this also";
    char *s=buf;
    unsigned wlen=0,len=strlen(s)-1;
    reverse(s,len);  //reverse entire string
    while(wlen<len){  // iterate over each word till end of string
      wlen=wordlen(s);
      reverse(s,wlen-1);
      s+=wlen+1;
      len-=wlen;
    }
    printf("%s\n",buf);
    return 0;
}

3
这是仅代码的答案。考虑添加对代码中发生的事情的解释。
贾斯汀

1

Python:得分2

几乎与霍华德的算法相同,但是反向执行相反的步骤(先翻转单词,然后翻转整个字符串)。使用附加的存储器是3字节的大小的变量:ij,和t。从技术上讲,find并且len正在使用一些内部变量,但是它们可以像轻松地重用ij没有任何功能丧失。

快速编辑:仅在字符不同时通过交换来节省字符串分配,因此我可以从注释2中获得一些额外的要点。

from sys import stdin

def word_reverse(string):
    # reverse each word
    i=0
    j=string.find(' ')-1
    if j == -2: j=len(string)-1
    while True:
        while i<j:
            if string[i] != string[j]:
                t = string[i]
                string[i] = string[j]
                string[j] = t
            i,j = i+1,j-1
        i=string.find(' ', i)+1
        if i==0: break
        j=string.find(' ', i)-1
        if j == -2: j=len(string)-1
    # reverse the entire string
    i=0
    j=len(string)-1
    while i<j:
        if string[i] != string[j]:
            t = string[i]
            string[i] = string[j]
            string[j] = t
        i,j = i+1,j-1
    return string

for line in stdin.readlines():
    # http://stackoverflow.com/a/3463789/1935085
    line = line.strip() # no trailing newlines ore spaces to ensure it conforms to '[a-z]+( [a-z]+)*'
    print word_reverse(bytearray(line))

1

批量

我承认我不完全理解得分(我认为是两个)。但是我会说- 它确实可以做到

@echo off

setLocal enableDelayedExpansion
set c=
set s=

for %%a in (%~1) do set /a c+=1 & echo %%a >> f!c!

for /L %%a in (!c!, -1, 1) do (
    set /p t=<f%%a
    set s=!s!!t!
    del f%%a
)

echo !s!

输入被视为第一个标准输入值,因此需要用引号引起来-
调用:script.bat "hello there everyone"
输出:everyone there hello

也许其他人可以给我打分(假设我没有以其他方式取消自己的资格)。


-2

Java脚本

function reverseWords(input) {
    if (input.match(/^[a-z]+( [a-z]+)*$/g)) {
        return input.split(' ').reverse().join(' ');
    }
}

用法:

> reverseWords('hello there everyone');
'everyone there hello'

我有一种奇怪的感觉,我错过了一些东西...


3
是的,由于您不修改输入字符串,因此此位置不正确。因为这在JavaScript中是不可能的,所以您需要使用字符数组(例如,代码点整数或单字符字符串)来模拟字符串。
马丁·恩德

更重要的是,它占用了大量额外的空间。
Ben Millwood
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.