检查字符串是否是字符串列表中的子字符串的最快方法

2023-11-27

我有一个包含 4000 个不同名字的静态列表:因此列表的长度很大(4000),但每个字符串大约有 4 到 12 个字符(它们是名字)。

然后,我有一个从数据库检索到的 10000 个字符串的动态列表:这些字符串可能具有任意长度。

我需要针对 10000 个字符串中的每一个输出该字符串是否包含 4000 个名称之一,如果包含,则输出哪一个。如果它包含多个名称,我只需要其中一个(即第一个)。而且,不太可能找到这样的名称,因此可能 10000 个中只有 10 个包含名称。

到目前为止我的代码:

names # list of 4000 short static names
fields # list of 10000 retrieved strings

def findit(element):
    for name in names:
        if name in element:
            return name
    return None

output = [findit(element) for element in fields]

这当然有效。但是,它非常慢,因为它不太可能找到名称,并且因为我正在测试是否是子字符串而不是相等(即我不能使用二等分或其他基于排序的索引技术)。它几乎每时每刻都会完整扫描所有名单。所以基本上,它执行大约 10000 x 4000 = 4000 万次“in”比较。

有没有一种算法可以优化这种搜索?


您可以考虑将名称列表转换为一个正则表达式。以这个小小的名单为例:

names = ['AARON',
    'ABDUL',
    'ABE',
    'ABEL',
    'ABRAHAM',
    'ABRAM',
    'ADALBERTO',
    'ADAM',
    'ADAN',
    'ADOLFO',
    'ADOLPH',
    'ADRIAN',
]

这可以用以下正则表达式表示:

\b(?:AARON|ABDUL|ABE|ABEL|ABRAHAM|ABRAM|ADALBERTO|ADAM|ADAN|ADOLFO|ADOLPH|ADRIAN)\b

但这不会非常有效。像树一样构建的正则表达式会更好地工作:

\b(?:A(?:B(?:E(?:|L)|RA(?:M|HAM)|DUL)|D(?:A(?:M|N|LBERTO)|OL(?:FO|PH)|RIAN)|ARON))\b

然后,您可以自动生成该正则表达式——可能首先创建一个dict- 名称列表中的树结构,然后将该树转换为正则表达式。对于上面的例子,中间树看起来像这样:

{
    'A': {
        'A': {
            'R': {
                'O': {
                    'N': {
                        '': {}
                    }
                }
            }
        }, 
        'B': {
            'D': {
                'U': {
                    'L': {
                        '': {}
                    }
                }
            }, 
            'E': {
                '': {}, 
                'L': {
                    '': {}
                }
            }, 
... etc

...可以选择简化为:

{
    'A': {
        'ARON': {
            '': {}
        }
        'B': {
            'DUL': {
                '': {}
            },
            'E': {
                '': {}, 
                'L': {
                    '': {}
                }
            },
            'RA': {
                'HAM': {
                    '': {}
                },
                'M': {
                    '': {}
                } 
            } 
        }, 

... etc

以下是执行此操作的建议代码:

import re 

def addToTree(tree, name):
    if len(name) == 0:
        return
    if name[0] in tree.keys():
        addToTree(tree[name[0]], name[1:])
    else:
        for letter in name:
            tree[letter] = {}
            tree = tree[letter]
        tree[''] = {}

# Optional improvement of the tree: it combines several consecutive letters into 
# one key if there are no alternatives possible
def simplifyTree(tree):
    repeat = True
    while repeat:
        repeat = False
        for key, subtree in list(tree.items()):
            if key != '' and len(subtree) == 1 and '' not in subtree.keys():
                for letter, subsubtree in subtree.items():
                    tree[key + letter] = subsubtree
                del tree[key]
                repeat = True
    for key, subtree in tree.items():
        if key != '': 
            simplifyTree(subtree)

def treeToRegExp(tree):
    regexp = [re.escape(key) + treeToRegExp(subtree) for key, subtree in tree.items()]
    regexp = '|'.join(regexp)
    return '' if regexp == '' else '(?:' + regexp + ')'

def listToRegExp(names):
    tree = {}
    for name in names:
        addToTree(tree, name[:])
    simplifyTree(tree)
    return re.compile(r'\b' + treeToRegExp(tree) + r'\b', re.I)

# Demo
names = ['AARON',
'ABDUL',
'ABE',
'ABEL',
'ABRAHAM',
'ABRAM',
'ADALBERTO',
'ADAM',
'ADAN',
'ADOLFO',
'ADOLPH',
'ADRIAN',
]

fields = [
    'This is Aaron speaking',
    'Is Abex a name?',
    'Where did Abraham get the mustard from?'
]

regexp = listToRegExp(names)
# get the search result for each field, and link it with the index of the field
results = [[i, regexp.search(field)] for i, field in enumerate(fields)]
# remove non-matches from the results
results = [[i, match.group(0)] for [i, match] in results if match]
# print results
print(results)

看到它运行repl.it

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

检查字符串是否是字符串列表中的子字符串的最快方法 的相关文章

随机推荐

  • 复选框不会在淘汰赛中被选中

    我有一个复选框和用于更新数据的复选框的单击事件 当我单击复选框时 数据正在更新 但复选框不会被选中 这是我的 html 代码 td td
  • 无效的代码签名权利[重复]

    这个问题在这里已经有答案了 我已遵循应用程序商店支持人员为寻求帮助而放置的所有程序 但每当我上传后提交应用程序时 状态都会变为 无效的二进制文件 并在邮件中显示以下消息 Invalid Code Signing Entitlements Y
  • 当所有类型不受支持时 HTML5 视频后备

    在 HTML5 规范中 它建议您将后备材料放入
  • 使用 JavaScript 或 jQuery 检测 Mac OS X 或 Windows 计算机的最佳方法

    因此 当用户使用 Mac 时 我尝试将 关闭 按钮移至左侧 而当用户使用 PC 时 将 关闭 按钮移至右侧 现在我通过检查用户代理来做到这一点 但它很容易被欺骗 无法进行可靠的操作系统检测 有没有可靠的方法来检测浏览器运行的操作系统是Mac
  • removeCallbacks 不停止可运行

    我从一个方法调用 myHandler postDelayed mMyRunnableHide 6000 其中调用 public Runnable mMyRunnableHide new Runnable public void run mT
  • ng-bootstrap - Typeahead 下拉宽度

    我开始使用 ng bootstrap Typeahead 组件 我对此非常满意 我想要实现的一件事是让下拉项具有与输入字段相同的宽度 而默认行为根据文本长度应用宽度 应该是基本的CSS 我创建了一个基本的Example在普朗克 正如您所注意
  • iOS 设备和模拟器的构建实际上有何不同?

    既然iOS模拟器是模拟器 为什么我需要专门为其构建呢 模拟器的重点不在于它运行real某种虚拟机 沙箱中的代码 那么 设备 模拟器构建方式的实际差异是什么 以及生成的应用程序有何不同 An application running nativ
  • Bouncy Castle scrypt 实现

    我目前正在使用以下方法实现密码哈希scrypt 我已经找到了一个不错的scryptGitHub 上的实现 令我惊讶的是我还发现了一个scryptBouncy Castle 库中的实施 该类没有记录 维基百科没有提到 Bouncy Castl
  • 64位和32位进程互通 boost::message_queue

    各位 美好的一天 我目前正在尝试找到一种在 64 位进程和 32 位进程之间传递数据的方法 由于它是一个实时应用程序并且两者都在同一台计算机上运行 因此我很难使用共享内存 shm 当我在寻找一些使用 shm 的同步机制时 我对 boost
  • Android:使用UIL和TouchImageView不显示ImageView

    我正在尝试从以下位置实现加载图像URL with Universal Image Loader and zoom with TouchImageView Mike Ortiz 但当尝试查看图像时 黑屏被展示 我已经检查过 URL 是否正确
  • Seaborn ImportError:DLL 加载失败:找不到指定的模块

    我收到 ImportError DLL 加载失败 找不到指定的模块 导入模块时seaborn 我尝试卸载seaborn和matplotlib 然后使用重新安装 pip install seaborn 但没有运气 我仍然遇到同样的错误 Imp
  • ora-06553 pls-306 调用“ogc_x”时参数数量或类型错误

    我正在尝试在 oracle 10g 中进行查询 事情是这样的 SELECT FROM h2h reg reg h2h cat estatus est WHERE reg FECH APLICACION SYSDATE AND REG ID
  • 使用 Hibernate Validator (JSR 303) 进行跨领域验证

    Hibernate Validator 4 x 中是否有跨字段验证的实现 或第三方实现 如果不是 那么实现跨字段验证器的最简洁方法是什么 例如 如何使用 API 来验证两个 bean 属性是否相等 例如验证密码字段与密码验证字段是否匹配 在
  • Jquery UI 可拖动不会调整其他 DIV 的大小

    在这嘭嘭嘭我有三个DIVs 除以另外两个DIV可拖动的 灰色 当可拖动时DIVs 向上 向下或向左 向右拖动 其他DIVs 应该调整大小 第一个可拖动 DIV 工作正常 左侧的 DIV 可以垂直调整其他 DIV 的大小 但第二个可拖动DIV
  • 如何在 SQL Server 非标准架构表上使用 dplyr tbl

    我的问题是我该如何使用dplyr函数 例如tbl 在不使用默认 dbo 架构的 SQL Server 表上 为了获得更多上下文 我尝试将此处给出的 R 数据库示例应用到我自己的表中 https db rstudio com 向下滚动到标题为
  • git Remote prune、git prune、git fetch --prune 等有什么区别

    我的情况是这样的 在同一个存储库上工作的人已经从他的本地和远程存储库中删除了一个分支 大多数在 Stack Overflow 或其他网站上询问此类问题的人都会遇到分支问题仍然显示在远程跟踪分支列表中的问题git branch a在底部 ma
  • 按 IN 序列对 MySQL 结果排序?

    当我使用 IN 从表中选择一组行时 例如 SELECT x y x z FROM x WHERE x id IN 23 55 44 12 有没有 SQL 技巧可以让它们按照 IN 集中给定的顺序返回 因此 在示例中 假设 x 具有 id 为
  • 垂直居中响应式 iframe

    我正在使用该技术此处描述使 iframe 视频 响应 本质上 iframe 绝对定位在宽度为 100 的包装元素内 包装元素根据视频的宽高比设置填充 embed responsive position relative video heig
  • $ 在 Haskell 中意味着什么/做什么?

    当您编写稍微复杂的函数时 我注意到 用得很多 但我不知道它的作用是什么 是中缀 应用程序 它定义为 a gt b gt a gt b f x f x or f x f x or id 它对于避免额外的括号很有用 f g x f g x 它的
  • 检查字符串是否是字符串列表中的子字符串的最快方法

    我有一个包含 4000 个不同名字的静态列表 因此列表的长度很大 4000 但每个字符串大约有 4 到 12 个字符 它们是名字 然后 我有一个从数据库检索到的 10000 个字符串的动态列表 这些字符串可能具有任意长度 我需要针对 100