查找两个字符串共享的所有 n 个字长子串的最大长度

2023-11-22

我正在制作一个Python脚本,它可以找到两个字符串共享的所有n个字长的子字符串的(最长可能)长度,忽略尾随标点符号。给定两个字符串:

“这是一个示例字符串”

“这也是一个示例字符串”

我希望脚本识别出这些字符串具有 2 个共同单词的序列(“this is”),后跟 3 个共同单词的序列(“示例字符串”)。这是我目前的方法:

a = "this is a sample string"
b = "this is also a sample string"

aWords = a.split()
bWords = b.split()

#create counters to keep track of position in string
currentA = 0
currentB = 0

#create counter to keep track of longest sequence of matching words
matchStreak = 0

#create a list that contains all of the matchstreaks found
matchStreakList = []

#create binary switch to control the use of while loop
continueWhileLoop = 1

for word in aWords:
    currentA += 1

    if word == bWords[currentB]:
        matchStreak += 1

        #to avoid index errors, check to make sure we can move forward one unit in the b string before doing so
        if currentB + 1 < len(bWords):
            currentB += 1

        #in case we have two identical strings, check to see if we're at the end of string a. If we are, append value of match streak to list of match streaks
        if currentA == len(aWords):
            matchStreakList.append(matchStreak)

    elif word != bWords[currentB]:

        #because the streak is broken, check to see if the streak is >= 1. If it is, append the streak counter to out list of streaks and then reset the counter
        if matchStreak >= 1:
            matchStreakList.append(matchStreak)
        matchStreak = 0

        while word != bWords[currentB]:

            #the two words don't match. If you can move b forward one word, do so, then check for another match
            if currentB + 1 < len(bWords):
                currentB += 1

            #if you have advanced b all the way to the end of string b, then rewind to the beginning of string b and advance a, looking for more matches
            elif currentB + 1 == len(bWords):
                currentB = 0
                break

        if word == bWords[currentB]:
            matchStreak += 1

            #now that you have a match, check to see if you can advance b. If you can, do so. Else, rewind b to the beginning
            if currentB + 1 < len(bWords):
                currentB += 1
            elif currentB + 1 == len(bWords):

                #we're at the end of string b. If we are also at the end of string a, check to see if the value of matchStreak >= 1. If so, add matchStreak to matchStreakList
                if currentA == len(aWords):
                    matchStreakList.append(matchStreak)
                currentB = 0
                break

print matchStreakList

该脚本正确输出公共字长子字符串 (2, 3) 的(最大)长度,并且对于迄今为止的所有测试都是如此。我的问题是:是否有一对两个字符串上述方法不起作用?更重要的是:是否有现有的 Python 库或众所周知的方法可用于查找两个字符串共享的所有 n 个字长的子字符串的最大长度?

[这个问题与最长公共子串问题不同,这只是我正在寻找的问题的一个特例(因为我想找到所有公共子串,而不仅仅是最长公共子串)。这个帖子建议诸如 1) 聚类分析、2) 编辑距离例程和 3) 最长公共序列算法等方法可能是合适的方法,但我没有找到任何可行的解决方案,而且我的问题可能比链接,因为我正在处理由空格限制的单词。]

EDIT:

我正在针对这个问题开始悬赏。为了防止对其他人有帮助,我想澄清一些要点。首先,@DhruvPathak 下面建议的有用答案并没有找到两个字符串共享的所有最大长 n 字长度的子字符串。例如,假设我们正在分析的两个字符串是:

“它们刚出生时都是洁白如一张一尘不染的纸 但它们将被每根鹅毛笔潦草地涂在上面并弄脏”

and

“当你第一次时,你全身都是白色的,一张可爱的、一尘不染的纸 出生;但你会被每只鹅的涂鸦和污迹 鹅毛笔”

在这种情况下,最大长 n 个字长度的子字符串列表(忽略尾随标点符号)为:

all
are
white a sheet of
spotless paper when
first are born but
are to be scrawled
and blotted by every

使用以下例程:

#import required packages
import difflib

#define function we'll use to identify matches
def matches(first_string,second_string):
    s = difflib.SequenceMatcher(None, first_string,second_string)
    match = [first_string[i:i+n] for i, j, n in s.get_matching_blocks() if n > 0]
    return match

a = "They all are white a sheet of spotless paper when they first are born but they are to be scrawled upon and blotted by every goose quill"
b = "You are all white, a sheet of lovely, spotless paper, when you first are born; but you are to be scrawled and blotted by every goose's quill"

a = a.replace(",", "").replace(":","").replace("!","").replace("'","").replace(";","").lower()
b = b.replace(",", "").replace(":","").replace("!","").replace("'","").replace(";","").lower()

print matches(a,b)

得到输出:

['e', ' all', ' white a sheet of', ' spotless paper when ', 'y', ' first are born but ', 'y', ' are to be scrawled', ' and blotted by every goose', ' quill']

首先,我不确定如何从该列表中选择仅包含整个单词的子字符串。其次,该列表不包括“are”,它是所需的最大长度公共 n 字长度子串之一。是否有一种方法可以找到这两个字符串共享的所有最大长 n 个字长的子字符串(“You are all...”和“These all are...”)?


这里仍然存在歧义,我不想花时间争论它们。但我想我还是可以添加一些有用的东西;-)

我写的是Python的difflib.SequenceMatcher,并花费了lot寻找预期情况的快速方法来找到最长公共子串的时间。理论上,这应该通过“后缀树”或用“最长公共前缀数组”增强的相关“后缀数组”来完成(如果您想通过 Google 搜索更多信息,引号中的短语是搜索词)。这些可以在最坏情况的线性时间内解决问题。但是,有时会出现这种情况,最坏情况的线性时间算法极其复杂和微妙,并且会受到很大的常数因素的影响 - 如果要搜索给定的语料库,它们仍然可以获得巨大的回报many次,但这不是 Python 的典型情况difflib而且看起来也不像你的情况。

无论如何,我在这里的贡献是重写SequenceMatcher's find_longest_match()返回方法all沿途找到的(局部)最大值匹配。笔记:

  1. 我将使用to_words()Raymond Hettinger 为您提供了函数,但没有转换为小写。转换为小写会导致输出与您所说的不完全一样。

  2. 然而,正如我在评论中已经指出的那样,这确实输出“quill”,它不在您所需的输出列表中。我不知道为什么不是,因为“羽毛笔”确实出现在两个输入中。

这是代码:

import re
def to_words(text):
    'Break text into a list of words without punctuation'
    return re.findall(r"[a-zA-Z']+", text)

def match(a, b):
    # Make b the longer list.
    if len(a) > len(b):
        a, b = b, a
    # Map each word of b to a list of indices it occupies.
    b2j = {}
    for j, word in enumerate(b):
        b2j.setdefault(word, []).append(j)
    j2len = {}
    nothing = []
    unique = set() # set of all results
    def local_max_at_j(j):
        # maximum match ends with b[j], with length j2len[j]
        length = j2len[j]
        unique.add(" ".join(b[j-length+1: j+1]))
    # during an iteration of the loop, j2len[j] = length of longest
    # match ending with b[j] and the previous word in a
    for word in a:
        # look at all instances of word in b
        j2lenget = j2len.get
        newj2len = {}
        for j in b2j.get(word, nothing):
            newj2len[j] = j2lenget(j-1, 0) + 1
        # which indices have not been extended?  those are
        # (local) maximums
        for j in j2len:
            if j+1 not in newj2len:
                local_max_at_j(j)
        j2len = newj2len
    # and we may also have local maximums ending at the last word
    for j in j2len:
        local_max_at_j(j)
    return unique

Then:

a = "They all are white a sheet of spotless paper " \
    "when they first are born but they are to be " \
    "scrawled upon and blotted by every goose quill"
b = "You are all white, a sheet of lovely, spotless " \
    "paper, when you first are born; but you are to " \
    "be scrawled and blotted by every goose's quill"

print match(to_words(a), to_words(b))

显示:

set(['all',
     'and blotted by every',
     'first are born but',
     'are to be scrawled',
     'are',
     'spotless paper when',
     'white a sheet of',
     'quill'])

编辑-它是如何工作的

许多序列匹配和比对算法最好理解为处理二维矩阵,具有计算矩阵条目并随后解释条目含义的规则。

对于输入序列a and b,画一个矩阵M with len(a)行和len(b)列。在这个应用程序中,我们想要M[i, j]包含以结尾的最长公共连续子序列的长度a[i] and b[j],计算规则为very easy:

  1. M[i, j] = 0 if a[i] != b[j].
  2. M[i, j] = M[i-1, j-1] + 1 if a[i] == b[j](我们假设越界矩阵引用默默地返回 0)。

在这种情况下解释也很容易:存在局部最大值非空比赛结束于a[i] and b[j],长度M[i, j],当且仅当M[i, j]是非零但是M[i+1, j+1]为 0 或越界。

您可以使用这些规则编写非常简单且紧凑的代码,其中包含两个循环,用于计算M正确地解决这个问题。缺点是代码将采取(最佳、平均和最坏情况)O(len(a) * len(b)) time and space.

虽然一开始可能会令人困惑,但我发布的代码正是执行上述操作。连接是模糊的,因为代码在多种方面针对预期情况进行了深度优化:

  • 而不是进行一次计算M,然后另一遍解释结果,计算和解释在一次传递中交错a.

  • 因此,不需要存储整个矩阵。相反,只有当前行(newj2len) 和上一行 (j2len)同时存在。

  • 因为这个问题的矩阵是usually大多数为零,这里的行通过将列索引映射到非零值的字典来稀疏地表示。零条目是“免费的”,因为它们永远不会显式存储。

  • 处理一行时,无需迭代每一列:预先计算的b2jdict 准确地告诉我们当前行中有趣的列索引(那些与当前行匹配的列)word from a).

  • 最后,部分出于偶然的是,所有前面的优化以这样一种方式共同进行:永远不需要知道当前行的索引,因此我们也不必费心计算它。

编辑-肮脏的简单版本

这是直接实现二维矩阵的代码,没有尝试优化(除了Counter通常可以避免显式存储 0 个条目)。它非常简单、简短和容易:

def match(a, b):
    from collections import Counter
    M = Counter()
    for i in range(len(a)):
        for j in range(len(b)):
            if a[i] == b[j]:
                M[i, j] = M[i-1, j-1] + 1
    unique = set()
    for i in range(len(a)):
        for j in range(len(b)):
            if M[i, j] and not M[i+1, j+1]:
                length = M[i, j]
                unique.add(" ".join(a[i+1-length: i+1]))
    return unique

当然;-) 返回与优化后相同的结果match()我一开始就发帖了。

编辑 - 另一个没有字典

只是为了好玩:-) 如果您掌握了矩阵模型,那么这段代码将很容易理解。关于这个特定问题的一个值得注意的事情是,矩阵单元的值仅取决于沿单元西北对角线的值。因此,从西部和北部边界的所有单元格向东南方向穿过所有主对角线就“足够好了”。这样,无论输入的长度如何,只需要小的常数空间:

def match(a, b):
    from itertools import chain
    m, n = len(a), len(b)
    unique = set()
    for i, j in chain(((i, 0) for i in xrange(m)),
                      ((0, j) for j in xrange(1, n))):
        k = 0
        while i < m and j < n:
            if a[i] == b[j]:
                k += 1
            elif k:
                unique.add(" ".join(a[i-k: i]))
                k = 0
            i += 1
            j += 1
        if k:
            unique.add(" ".join(a[i-k: i]))
    return unique
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

查找两个字符串共享的所有 n 个字长子串的最大长度 的相关文章

随机推荐

  • 如何在 Chrome 的弹出窗口上切换设备模式?

    我有一个网络应用程序 其中的聊天功能会在新的弹出窗口中打开 通常 在 Chrome 中我可以按 F12 并单击智能手机的图标来切换它 但在弹出的窗口中并没有出现该图标 这对我来说很重要 因为我需要限制弹出窗口的连接以模拟用户从聊天中断开连接
  • 如何在 GCC 中指定枚举大小?

    我想为枚举指定 64 位的枚举大小 这怎么可能通过 GCC 实现呢 该代码不需要 可移植 因为我只对使代码在 x86 32 和 x86 64 Linux 的 GCC 编译上工作感兴趣 这意味着任何可以提供我想要的功能的黑客都可以 只要它适用
  • 处理urllib2的超时? - Python

    我在 urllib2 的 urlopen 中使用超时参数 urllib2 urlopen http www example org timeout 1 我如何告诉Python 如果超时到期 应该引发自定义错误 有任何想法吗 您想要使用的情况
  • 为什么我的区域特定 Web API 可以从所有其他区域访问?

    我目前正在开发一个 ASP NET MVC 4 Web 应用程序项目 该项目必须遵守以下设计决策 主 MVC 应用程序位于解决方案的根目录中 所有管理员功能都位于单独的区域中 每个外部方 例如供应商 都有自己的区域 每个区域 包括根部 都构
  • 什么是 ruby​​ on Rails?

    我是一名前端开发人员 HTML CSS JS 和 jQuery 我了解一点 PHP 我正在尝试了解 Ruby Ruby on Rails 是什么 On http rubyonrails org 它说 Ruby on Rails 是一个开源
  • C++11 字符串开头的大小写不敏感比较(unicode)

    我必须检查特定字符串是否以另一个字符串开头 字符串使用 utf8 编码 比较应不区分大小写 我知道这与那个主题非常相似C 中不区分大小写的字符串比较但我不想使用 boost 库 我更喜欢便携式解决方案 如果 几乎 不可能 我更喜欢面向 Li
  • Angularjs 获取巨大 json 文件的请求

    我需要向用户显示数据库中的一些数据 数据位于 json 文件中 并且大小相当大 json 文件的大小大约在 15MB 左右 我创建了一个服务并使用 Promise api 发出成功的请求并加载数据并通过在 div 上执行 ng repeat
  • SFINAE 与以下 has_member 函数一起无法正常工作是什么?

    我正在尝试以下示例沃尔特 布朗 Walter Brown 的 TMP 演讲我正在努力得到他的has member实施工作 然而 实现似乎错误地返回 true 这让我相信 SFINAE 有一些我不理解的细节 include
  • 在 MATLAB 中使用 imshow 方法显示图像标题

    如何在 MATLAB 图形中显示图像标题 我有以下代码 I imread images pap png subplot 1 2 1 imshow I here I want to show labels Use the title命令 它的
  • 用于开发新的Windows Azure管理门户的框架?

    有谁知道微软使用什么框架在Windows Azure上开发类似Metro的Web管理门户 如果是这样 开发者可以使用吗 I 提出了同样的问题并因此受到很多仇恨 获胜的答案是地铁用户界面包 它完成了 Azure 中的许多工作 但您必须自己实现
  • 如何在 java 中使 JTable 可编辑

    我在 java 中使用 JTable 但它不允许我编辑单元格 private final TableModel dataModel new AbstractTableModel public int getColumnCount retur
  • proxyMode ScopedProxyMode.TARGET_CLASS 与 ScopedProxyMode.INTERFACE

    正如其他 SO 答案所建议的 根据您的需要使用代理模式类型 我仍然很困惑 Configuration ComponentScan public class Application public static void main String
  • Python:更新线程中的参数

    我想知道当该参数在程序主体中获得新值时是否可以启动一个新线程并更新其参数 所以像这样 i 0 def foo i print i time sleep 5 thread start new thread foo i while True i
  • SQL 选择 MAX(COUNT)

    我正在尝试选择具有 MAX 微帖子数的用户 SELECT name count FROM users INNER JOIN microposts ON microposts user id users id GROUP BY users i
  • IntegrityError:错误:列“user_id”中的空值违反了非空约束

    使用 postgres PostgreSQL 9 4 5 我刚刚迁移了一个sqlite3数据库到一个postgresqlD b 由于某种原因 自从这次迁移以来 当我尝试创建用户时 出现了有关user id 这是主键 正在被提升 以前这不是问
  • 当泛型类型设置为 never 时,泛型条件类型解析为 never

    我需要一个泛型类型 当 该属性的 泛型参数为时 该类型可以从指定类型中排除泛型属性never 为了实现这一点 我使用了Omit和条件类型 例如 当通用参数设置为number它的行为符合预期 但是当泛型类型设置为never 类型解析为neve
  • 更改 eclipse 创建 .eclipse、.p2 和其他文件夹的位置

    我看到 eclipse 在我的用户主文件夹中创建了一些文件夹 例如 eclipse p2 等 我想更改此默认文件夹 我想将所有内容保存在 D 位置 我读了这个在 Linux 中更改 eclipse 文件夹但我不明白我必须更改哪个文件 ini
  • 如何在 SQL 中创建 REPLACE PATTERN?

    我有一个很长的 NVARCHAR 变量 我需要在其中替换一些模式 如下所示 DECLARE data NVARCHAR 200 Hello PAT1 stackoverflow PAT2 world PAT3 我需要全部更换 PAT 带有空
  • 分配给设备内存的 CUDA 全局(如 C 语言)动态数组

    因此 我尝试编写一些利用 Nvidia CUDA 架构的代码 我注意到与设备进行复制确实损害了我的整体性能 因此现在我尝试将大量数据移动到设备上 由于这些数据被用于许多函数 我希望它是全局的 是的 我可以传递指针 但我真的很想知道在这种情况
  • 查找两个字符串共享的所有 n 个字长子串的最大长度

    我正在制作一个Python脚本 它可以找到两个字符串共享的所有n个字长的子字符串的 最长可能 长度 忽略尾随标点符号 给定两个字符串 这是一个示例字符串 这也是一个示例字符串 我希望脚本识别出这些字符串具有 2 个共同单词的序列 this