如何找到两个字符串的并集并保持顺序

4

我有两个字符串,想要找到它们的并集。在此过程中,我希望保持顺序。我的目的是尝试使用不同的方式来OCR图像并获得不同的结果。我希望将所有不同的结果组合成一个包含最多内容的结果。

至少这是我的目标:

#example1
string1 = "This is a test trees are green roses are red"
string2 = "This iS a TEST trees 12.48.1952 anthony gonzalez"
finalstring = "this is a test trees are green roses are red 12.48.1952 anthony gonzalez" 

#example2
string2 = "This is a test trees are green roses are red"
string1 = "This iS a TEST trees 12.48.1952 anthony gonzalez"
finalstring = "this is a test trees are green roses are red 12.48.1952 anthony gonzalez"

#example3
string1 = "telephone conversation in some place big image on screen"
String2 = "roses are red telephone conversation in some place big image on screen"
finalstring = "roses are red telephone conversation in some place big image on screen"
#or the following - both are fine in this scenario.
finalstring = "telephone conversation in some place big image on screen roses are red "

这是我尝试过的方法:

>>> string1 = "This is a test trees are green roses are red"
>>> string2 = "This iS a TEST trees 12.48.1952 anthony gonzalez"
>>> list1 = string1.split(" ")
>>> list2 = string2.split(" ")
>>> " ".join(list(set(list1) | set(list2))).lower()
'a gonzalez this is trees anthony roses green are test 12.48.1952 test is red'

你想按单词还是按字符进行操作(后者会产生非常不同的结果)。另外,显然您想忽略大小写? - poke
使用内置的difflib进行基于字符的比较,可能得到的结果是'This is a test trees 12.48.1952 arenthony gronzaleen roses are redz' - poke
你有没有想过如何处理“foo bar”和“bar foo”的并集顺序的逻辑? - hilberts_drinking_problem
@YakymPirozhenko 是的,肯定会有重复的单词。就像我给出的例子一样。单词“这是一个测试树”是重复的。 - Anthony
你说你想要保持顺序,但在“屏幕上”的示例中,你只是将第二个字符串中的额外单词从中间截断,所以你忽略了这些额外单词在该字符串中出现的顺序。 - poke
显示剩余3条评论
3个回答

5
你可以使用 difflib.SequenceMatcher 来完成这个任务:
import difflib
def merge (l, r):
    m = difflib.SequenceMatcher(None, l, r)
    for o, i1, i2, j1, j2 in m.get_opcodes():
        if o == 'equal':
            yield l[i1:i2]
        elif o == 'delete':
            yield l[i1:i2]
        elif o == 'insert':
            yield r[j1:j2]
        elif o == 'replace':
            yield l[i1:i2]
            yield r[j1:j2]

使用方法如下:

>>> string1 = 'This is a test trees are green roses are red'
>>> string2 = 'This iS a TEST trees 12.48.1952 anthony gonzalez'

>>> merged = merge(string1.lower().split(), string2.lower().split())
>>> ' '.join(' '.join(x) for x in merged)
'this is a test trees are green roses are red 12.48.1952 anthony gonzalez'

如果您想在字符级别上执行合并操作,可以直接修改调用以操作字符串(而不是单词列表):
>>> merged = merge(string1.lower(), string2.lower())
>>> ''.join(merged)
'this is a test trees 12.48.1952 arenthony gronzaleen roses are redz'

此解决方案正确保持了字符串的各个部分的顺序。因此,如果两个字符串以相同的部分结尾但在末尾之前有不同的段落,则这些不同的段落仍将在结果中出现在共同末尾之前。例如将A B DA C D合并后,您将获得A B C D
因此,您可以通过简单地删除结果字符串的一部分来找到每个原始字符串的正确顺序。如果从上述示例结果中删除C,则会返回第一个字符串;如果删除B,则会返回第二个字符串。而且,这也适用于更复杂的合并。

如果在这里交换字符串1和字符串2,结果是:This iS a TEST trees 12.48.1952 anthony gonzalez are green roses。然而,在那种情况下,我追求的也是这是测试树是绿色的玫瑰是红色的12.48.1952安东尼·冈萨雷斯。 - Anthony
@Anthony 我代码中的合并算法优先考虑左边的字符串。如果您想要不同的行为,您需要指定一些规则来选择其中一个。现在,它完全独立于内容。 - poke
看了这个合并解决方案和问题中添加的示例 - 我认为合并应该始终发生在字符串的末尾... - Anthony
那么这并不是真正的合并。你只是将第二个字符串中缺失于第一个字符串的单词添加到结果的末尾?所以你更想要的是 string1 + ' '.join(w for w in string2.split() if w not in string1.split()) - poke

1
请勿使用集合。正如您所注意到的,只有一个“are”作为set()仅保留唯一对象而出现在最终结果中。
string1 = "This is a test trees are green roses are red"
string2 = "This iS a TEST trees 12.48.1952 anthony gonzalez"

str_lst = string1.split()

for s, t in zip(string1.split(), string2.split()):
    if s.lower() == t.lower():
        continue
    else:
        str_lst.append(t)

string = " ".join(s.lower() for s in str_lst)
#this is a test trees are green roses are red 12.48.1952 anthony gonzalez

如果您在此处交换string1和string2,则结果为:“This iS a TEST trees 12.48.1952 anthony gonzalez are green roses”。但是,在这种情况下,我仍然想要“this is a test trees are green roses are red 12.48.1952 anthony gonzalez”。 - Anthony
这种方法无法处理字符串末尾的匹配项,例如 'foo bar''baz bar' 的结果是 'foo bar baz',但实际上应该得到类似于 'foo baz bar' 的结果。 - poke

0
" ".join(x if i >= len(string2.split()) or x == string2.lower().split()[i] else " ".join((x, string2.split()[i])) for i, x in enumerate(string1.lower().split()))

你可以使用生成器推导式和 join 来实现你想要的功能。这将把 i 设置为 string1 中单词的索引,将 x 设置为该单词。然后它检查该单词是否在 string2 中,如果不在,则将 string2 中的单词添加到 i 的位置以将两个单词放入最终字符串中。

我正在尝试理解它。我很难阅读它,但是它给了我这个结果:>>> "".join(x if x not in string2.lower() else " ".join((x, string2.split()[i])) for i, x in enumerate(string1.lower().split())) 'this Thisis iSa atest TESTtrees treesaregreenrosesarered' - Anthony
@Anthony 抱歉没有进行任何测试,已经修复了。 - Natecat
这似乎不能正确地删除内部匹配项中的重复项,例如对于字符串 'x y x''y',您会得到一个结果为 'x y y x' - poke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接