如何有效地覆盖两个字符串,其中一个字符串优先于另一个字符串?

时间:2017-09-18 13:33:09

标签: python range overlap

我有两个字符串作为文件范围的缓冲区。我想从这个“文件”中读取,以便从第一个字符串中优先读取第二个字符串中的字节,其中存在重叠。

在下面的示例中,r1和r2分别表示文件的范围,包括字符串以及开始和结束偏移。我已经格式化了这些示例,以便更清楚地在文件中存在字符串的位置。

def prioritized_read(range1, range2, read_start, read_end):
    # This is the bit I don't know how to write

r1 = ("ABCDEF", (0,6))
r2 = ( "DEF",   (1,4))
assert prioritized_read(r1, r2, 0, 6) == "ABCDEF"

r1 = ("ABC",    (0,3))
r2 = ( "DEF",   (1,4))
assert prioritized_read(r1, r2, 1, 4) == "BCF"

r1 = (  "ABC",  (2,5))
r2 = ("DEF",    (0,3))
assert prioritized_read(r1, r2, 0, 4) == "DEAB"

r1 = ( "A",     (1,2))
r2 = ("DEF",    (0,3))
assert prioritized_read(r1, r2, 0, 3) == "DAF"

r1 = ("ABC",    (0,3))
r2 = (   "DEF", (3,6))
assert prioritized_read(r1, r2, 3, 6) == "DEF"

read_startread_end将始终受r1r2的端点限制。

这里的示例范围很小,但在我的应用程序中它们可能超过10亿,所以我正在寻找一种时间和内存效率的解决方案。

我考虑将此帖子发布到Programming Puzzles & Code Golf。看起来它应该是一个简单而明显的过程......但它击败了我。

N.B。我不是真的在这里读取文件,所以我不能使用涉及Python文件对象的解决方案。我只是使用文件作为一个方便的类比。

3 个答案:

答案 0 :(得分:0)

这样的东西可以提供所需的输出:

def prioritized_read(r1, r2, start, end):
    s = [''] * max(r1[1][1], r2[1][1])
    s[r2[1][0]:r2[1][1]] = r2[0]
    s[r1[1][0]:r1[1][1]] = r1[0]

    return ''.join(s[start:end])

这假定其中一个范围始终以0开头,并且范围不是不相交的。如果范围很大,这可能不是内存效率

答案 1 :(得分:0)

应将间隔划分为子间隔。让我在这个例子中解释一下:

r1 = (  "ABC",  (2,5))
r2 = ("DEF",    (0,3))
assert prioritized_read(r1, r2, 0, 4) == "DEAB"

子区间的边界是开始,结束数字,即:0,2,3,4,5。 read_start之前和read_end之前的数字可以忽略,因此5表示输出。我们现在0,2,3,4。不太明显的是,range2内的range1边界也可以被忽略,因此3也是如此。边界为0,2,4,这意味着子信息为0-22-4

其余的很容易。我们从具有所需范围的数据源中读取 - 尊重优先级。范围不是从零开始,因此必须考虑偏移。

def prioritized_read(range1, range2, read_start, read_end):
    d1, r1 = range1
    d2, r2 = range2
    bset = set((read_start, read_end))
    for b in r1: 
        if read_start < b < read_end:
            bset.add(b)
    for b in r2: 
        if not r1[0] < b < r1[1] and read_start < b < read_end:
            bset.add(b)
    boundaries = sorted(bset)

    output = []
    for i in range(len(boundaries) - 1): 
        start, end = boundaries[i], boundaries[i+1]
        if r1[0] <= start < r1[1]:
            # read from #1
            output.append(d1[start-r1[0]:end-r1[0]])
        elif r2[0] <= start < r2[1]: 
            # read from #2
            output.append(d2[start-r2[0]:end-r2[0]])
        else:
            raise ValueError("no data source for {}:{}".format(start, end))
    return "".join(output)

答案 2 :(得分:-1)

您可以创建所需长度的数组,然后在正确的位置复制range2range1的字符。

所有测试都通过了这个:(编辑:用切片赋值替换了丑陋的循环。)

def _override_chars(array, string, indices, read_start, read_end):
    substring_start = max(0, read_start - indices[0])
    substring_end = read_end - indices[0]
    substring = string[substring_start:substring_end]

    insertion_start = max(0, indices[0] - read_start)
    insertion_end = insertion_start + len(substring)
    array[insertion_start:insertion_end] = substring


def prioritized_read(range1, range2, read_start, read_end):
    a = [None] * (read_end - read_start)
    _override_chars(a, *range2, read_start, read_end)
    _override_chars(a, *range1, read_start, read_end)
    return ''.join(a)

编辑: 另一种解决方案是构建一个发电机。它的内存效率更高,因为您不会同时在内存中同时保存数组和最终字符串。

def _prioritized_read(range1, range2, read_start, read_end):
    string1, (start1, end1) = range1
    string2, (start2, end2) = range2
    for i in range(read_start, read_end):
        if start1 <= i < end1:
            yield string1[i - start1]
        elif start2 <= i < end2:
            yield string2[i - start2]

def prioritized_read(range1, range2, read_start, read_end):
    return ''.join(_prioritized_read(range1, range2, read_start, read_end))