【问题标题】:Match string with a list to retrieve iteratively将字符串与列表匹配以迭代检索
【发布时间】:2017-10-22 23:49:38
【问题描述】:

有两个列表,一个由一个句子的字符序列组成,另一个由单词组成。

目标是将第一个列表的项目与第二个列表的项目匹配length of li_a (len(li_a)) 次。相同的匹配词将被暂时保存为候选词。在最后的迭代过程之后,最长的单词将被选为我们的预期结果,并附加到一个新的列表中。

由于li_a 中有18 个字符,假设literation time18

li_a = ['T','h','o','m','a','s','h','a','d','a','h','a','r','d','t','i','m','e']
li_words = ['a','The','Thomas','have','had','has','hard','hot','time','tea']

首先,li_a 中的第一项与li_words 匹配。

1. 'T' => li_words || li_a[0] => li_words
2. 'Th' => li_words || li_a[0]+li_a[1] => li_words
3. 'Tho' => li_words || li_a[0]+li_a[1]+li_a[2] => li_words
...
6. 'Thomas' => li_words || li_a[0]+..+li_a[5] => li_words (marks as candidate when the match is found)
...
18. 'Thomashadahardtime' => li_words || li_a[0]..li_a[17] => li_words

上面的例子展示了第一个迭代过程应该如何完成。它为我们提供了一个候选结果,即Thomas。但是接下来,li_a从第一个'T'到's'(Thomas)的项目会被扣除,

li_a = ['h','a','d','a','h','a','r','d','t','i','m','e']

应该像之前一样执行第二次迭代过程来检索下一个单词。

最后,我们的列表最终结果应该是这样的:

final_li = ['Thomas','had','a','hard','time']

尝试

以下尝试适用于查找最长匹配项,但不适用于迭代工作,并且当li_words 中缺少单词时不会给出准确的结果

def matched_substring(li1, li2):
    new_li = []
    tmp = ''
    for a in li1:
        tmp += a
        count = 0
        for b in li2:
            if tmp == b:
                count += 1
        if count == 0:
            tmp1 = tmp.replace(a, '')
            new_li.append(tmp1)
            tmp = a
    if li2.__contains__(tmp):
        new_li.append(tmp) 
    return new_li

它返回为:['Thomas', 'h', 'a', 'd', 'a', 'h', 'a', 'r', 'd', 't', 'i', 'm']

UNICODE 中的字符

string_a = "['ဒီ|စစ်|ဆေး|မှု|ကို|သီး|ခြား|လွတ်|လပ်|တဲ့|ပု|ဂ္ဂို|လ်|တ|ဦး|က|ဦး|ဆောင်|ခိုင်း|တာ|ဟာ|လူ|ထု|အ|ကျိုး|အ|တွက်|ဖြစ်|တယ်|လို့|တ|ရား|ရေး|ဝန်|ကြီး|ဌာ|န|က|ထုတ်|ပြန်|တဲ့|ကြေ|ညာ|ချက်|ထဲ|မှာ|ဖေါ်|ပြ|ထား|ပါ|တယ်']"

将上面的字符串转换为列表:

##Get rid of brackets & punctuation marks
strp_str = string_a.strip("[]")
strp_str = strp_str.strip("'")
##Now we achieve *li_a* 
li_a = strp_str.split('|')

li_words 列表的剪贴板链接:mm-words.txt

##Get all the words in List
read_words = open('mm-words.txt','r')
##Achieve them in List    
li_words = read_words.read().split('\n')

##Now run into function
print analyze(li_a, li_words)

【问题讨论】:

  • 由于该问题具有等效的子问题,因此这是探索递归或动态编程方法的绝佳机会。
  • 我认为在li_words 中做循环项目并在li_a 中匹配项目会很容易

标签: python list


【解决方案1】:

为了获得所有可能的解决方案(如果还有一些“重叠”的单词),可能首先为每个单词找到该特定单词可能出现的所有起始位置。然后策略是从字符串的开头开始,测试所有可能出现在此处的候选字符,并将字符串中相应数量的字符向前移动。在这个新位置,重复该过程(由于递归,所有组合都被探索)。一旦我们到达字符串的末尾,就找到了一个成功的解决方案。

li_a = ['T', 'h','o','m','a','s','h','a','d','a','h','a','r','d','t','i','m','e','a']
li_words = ['a','The','Thomas','have','had','has','hard','hot','time','tea','timea']


def analyze(li_a, li_words):

    s = ''.join(li_a)

    #for each word, find all its positions in s
    stat = {}
    for word in li_words:
        pos = s.find(word)
        while pos != -1:
            if not pos in stat:
                stat[pos] = []
            stat[pos].append(word)
            pos = s.find(word, pos + 1)

    solutions = []

    def check(idx, solution):
        if idx == len(s):
            solutions.append(solution)
            return

        #at position idx, test all candidates and call
        #itself recursively
        words = stat.get(idx, [])
        for word in words:
            check(idx + len(word), solution + [word])

    #start at the beginning
    check(0, [])

    return solutions

print(analyze(li_a, li_words))

编辑

我测试了您的 Unicode 输入,似乎 string_a 包含一个单词 ဖေါ်,而 mm-words.txt 中缺少该单词。另一方面,在这种情况下,建议的解决方案无论如何都会失败。首先,pos = s.find(word, pos + len(word)) 中有一个错误,它应该是pos = s.find(word, pos + 1),以便查找特定单词的所有重叠出现。更重要的是,可恶的复杂性要求(本质上是指数缩放)使其无法在如此大的输入上使用。要走的路是采用dynamic programming approach。在下面的示例中,我只取了string_a 中的前10 个单词(保存到str.txt),以避免丢失一个:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

def read_data(fname, delim, uniq = False):
    with open(fname, 'r') as F:
        data = F.read().split(delim)
        data = map(lambda s: s.decode('utf-8').strip(), data)
        data = filter(lambda s: s, data)
        if uniq:
            data = list(set(data))
    return data

li_a = read_data('str.txt', '|')
li_words = read_data('mm-words.txt', '\n', uniq = True)

def analyze(li_a, li_words):
    words = set(li_words)

    s = ''.join(li_a[0:10])
    N = len(s)

    solutions = [ [] for idx in range(0, N) ]
    S = [False for idx in range(0, N)]

    for i in range(0, N):
        flag = False
        if (s[0:i+1] in words):
            flag = True
            solutions[i].append([s[0:i+1], -1])

        else:
            for j in range(1, i+1):
                if S[j-1] and (s[j:i+1] in words):
                    #save the newly identified word and reference to solution
                    #previously found at location j-1
                    solutions[i].append([s[j:i+1], j-1])

                    #break #find only one solution
                    flag = True
        S[i] = flag

    splittings = []
    def assemble(pos, L):
        if pos == -1:
            splittings.append(L)
            return
        for w,idx in solutions[pos]:
            assemble(idx, [w] + L)

    assemble(N-1, [])
    return splittings

splittings = analyze(li_a, li_words)
for splitting in splittings:
    print(' | '.join(splitting).encode('utf-8'))

这会产生:

ဒီ | စစ်ဆေး | မှု | ကို | သီးခြား | လွတ်လပ် | တဲ့
ဒီ | စစ် | ဆေး | မှု | ကို | သီးခြား | လွတ်လပ် | တဲ့
ဒီ | စ | စ် | ဆေး | မှု | ကို | သီးခြား | လွတ်လပ် | တဲ့
ဒီ | စစ်ဆေး | မှု | ကို | သီး | ခြား | လွတ်လပ် | တဲ့
ဒီ | စစ် | ဆေး | မှု | ကို | သီး | ခြား | လွတ်လပ် | တဲ့
ဒီ | စ | စ် | ဆေး | မှု | ကို | သီး | ခြား | လွတ်လပ် | တဲ့
ဒီ | စစ်ဆေး | မှု | ကို | သီးခြား | လွတ် | လပ် | တဲ့
ဒီ | စစ် | ဆေး | မှု | ကို | သီးခြား | လွတ် | လပ် | တဲ့
ဒီ | စ | စ် | ဆေး | မှု | ကို | သီးခြား | လွတ် | လပ် | တဲ့
ဒီ | စစ်ဆေး | မှု | ကို | သီး | ခြား | လွတ် | လပ် | တဲ့
ဒီ | စစ် | ဆေး | မှု | ကို | သီး | ခြား | လွတ် | လပ် | တဲ့
ဒီ | စ | စ် | ဆေး | မှု | ကို | သီး | ခြား | လွတ် | လပ် | တဲ့
ဒီ | စစ်ဆေး | မှု | ကို | သီးခြား | လွ | တ် | လပ် | တဲ့
ဒီ | စစ် | ဆေး | မှု | ကို | သီးခြား | လွ | တ် | လပ် | တဲ့
ဒီ | စ | စ် | ဆေး | မှု | ကို | သီးခြား | လွ | တ် | လပ် | တဲ့
ဒီ | စစ်ဆေး | မှု | ကို | သီး | ခြား | လွ | တ် | လပ် | တဲ့
ဒီ | စစ် | ဆေး | မှု | ကို | သီး | ခြား | လွ | တ် | လပ် | တဲ့
ဒီ | စ | စ် | ဆေး | မှု | ကို | သီး | ခြား | လွ | တ် | လပ် | တဲ့
ဒီ | စစ်ဆေး | မှု | ကို | သီးခြား | လွတ် | လ | ပ် | တဲ့
ဒီ | စစ် | ဆေး | မှု | ကို | သီးခြား | လွတ် | လ | ပ် | တဲ့
ဒီ | စ | စ် | ဆေး | မှု | ကို | သီးခြား | လွတ် | လ | ပ် | တဲ့
ဒီ | စစ်ဆေး | မှု | ကို | သီး | ခြား | လွတ် | လ | ပ် | တဲ့
ဒီ | စစ် | ဆေး | မှု | ကို | သီး | ခြား | လွတ် | လ | ပ် | တဲ့
ဒီ | စ | စ် | ဆေး | မှု | ကို | သီး | ခြား | လွတ် | လ | ပ် | တဲ့
ဒီ | စစ်ဆေး | မှု | ကို | သီးခြား | လွ | တ် | လ | ပ် | တဲ့
ဒီ | စစ် | ဆေး | မှု | ကို | သီးခြား | လွ | တ် | လ | ပ် | တဲ့
ဒီ | စ | စ် | ဆေး | မှု | ကို | သီးခြား | လွ | တ် | လ | ပ် | တဲ့
ဒီ | စစ်ဆေး | မှု | ကို | သီး | ခြား | လွ | တ် | လ | ပ် | တဲ့
ဒီ | စစ် | ဆေး | မှု | ကို | သီး | ခြား | လွ | တ် | လ | ပ် | တဲ့
ဒီ | စ | စ် | ဆေး | မှု | ကို | သီး | ခြား | လွ | တ် | လ | ပ် | တဲ့

【讨论】:

  • 我已经尝试过代码,它就像一个魅力,但是当我尝试使用 Unicode 字符时它返回空列表。这种情况有什么建议吗?感谢您的出色工作。
  • 我很高兴 :) 你使用的是 Python2,我想?你能发布产生空输出的特定li_a/li_words吗?这很可能与在这种情况下如何解释 len 有关(即字节数与字符数)...
  • 感谢您对我的包容。我用 UNICODE 中的字符 扩展了帖子以获取详细信息。在此先感谢:)
  • @htetmyet 我已经更新了解决测试数据的答案,原来的方法太慢了,所以有必要采取稍微不同的策略......
  • @htetmyet 是的,我想避免出现在您发布的单词列表中的ဖေါ်(所以在这种情况下它找不到解决方案)
【解决方案2】:

也许你可以尝试匹配li_a生成的字符串,例如

>>> li_a = ['T','h','o','m','a','s','h','a','d','a','h','a','r','d','t','i','m','e']
>>> li_words = ['a','The','Thomas','have','had','has','hard','hot','time','tea']
>>>
>>> s = "".join(li_a)
>>> for i in sorted(li_words,key=lambda x:-len(x)):
...     if i in s:
...         s=s.replace(i,str(li_words.index(i))+",")
...
>>> [li_words[int(i)] for i in s[:-1].split(",")]
['Thomas', 'had', 'a', 'hard', 'time']

希望这会有所帮助。

【讨论】:

  • 感谢您的快速回复,但出现ValueError: invalid literal for int() with base 10:的错误
  • 我的代码运行良好,当您使用int(i) 时,请确保i 不是空字符串。
  • 我试过你的代码。它适用于英文字母,但它表示 ValueError: invalid literal for int() with base 10: 用于 unicode 字符。
【解决方案3】:

这里你有一个使用itertools的函数式方法:

import itertools
li_a = ['T','h','o','m','a','s','h','a','d','a','h','a','r','d','t','i','m','e']
li_words = ['a','The','Thomas','have','had','has','hard','hot','time','tea']
res = list(itertools.imap(lambda x: max(itertools.ifilter(lambda y: x in y, li_words), key=len), li_a))
res
['Thomas', 'Thomas', 'Thomas', 'Thomas', 'Thomas', 'Thomas', 'Thomas', 'Thomas', 'hard', 'Thomas', 'Thomas', 'Thomas', 'hard', 'hard', 'time', 'time', 'Thomas', 'have']

我们的想法是,对于li_a 中的每个字母,我们过滤li_words 中的哪个单词,然后在该集合中使用max,使用len 来获取最大的单词。

这里是每个字母的匹配压缩:

zip(li_a, res)
[('T', 'Thomas'), ('h', 'Thomas'), ('o', 'Thomas'), ('m', 'Thomas'), ('a', 'Thomas'), ('s', 'Thomas'), ('h', 'Thomas'), ('a', 'Thomas'), ('d', 'hard'), ('a', 'Thomas'), ('h', 'Thomas'), ('a', 'Thomas'), ('r', 'hard'), ('d', 'hard'), ('t', 'time'), ('i', 'time'), ('m', 'Thomas'), ('e', 'have')]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-09
    • 1970-01-01
    • 2013-06-18
    • 1970-01-01
    • 2021-08-08
    • 2021-11-30
    相关资源
    最近更新 更多