两个以上字符串中最长的公共子字符串-Python

两个以上字符串中最长的公共子字符串-Python,python,string,longest-substring,Python,String,Longest Substring,我正在寻找一个Python库,用于从一组字符串中查找最长的公共子字符串。有两种方法可以解决此问题: 使用后缀树 使用动态规划 实现的方法并不重要。重要的是,它可以用于一组字符串(而不仅仅是两个字符串)。def common_前缀(字符串): “”“查找作为所有字符串前缀的最长字符串。 """ 如果不是字符串: 返回“” 前缀=字符串[0] 对于字符串中的s: 如果len(s)len(substr)并且是任意(数据[0][i:i+j],数据)的子对象: substr=数据[0][i:i+j]

我正在寻找一个Python库,用于从一组字符串中查找最长的公共子字符串。有两种方法可以解决此问题:

  • 使用后缀树
  • 使用动态规划
实现的方法并不重要。重要的是,它可以用于一组字符串(而不仅仅是两个字符串)。

def common_前缀(字符串):
“”“查找作为所有字符串前缀的最长字符串。
"""
如果不是字符串:
返回“”
前缀=字符串[0]
对于字符串中的s:
如果len(s)

中,这些成对函数将在任意字符串数组中找到最长的公共字符串:

def long_substr(data):
    substr = ''
    if len(data) > 1 and len(data[0]) > 0:
        for i in range(len(data[0])):
            for j in range(len(data[0])-i+1):
                if j > len(substr) and is_substr(data[0][i:i+j], data):
                    substr = data[0][i:i+j]
    return substr

def is_substr(find, data):
    if len(data) < 1 and len(find) < 1:
        return False
    for i in range(len(data)):
        if find not in data[i]:
            return False
    return True


print long_substr(['Oh, hello, my friend.',
                   'I prefer Jelly Belly beans.',
                   'When hell freezes over!'])
希望这有帮助

杰森


免责声明这对雅克的回答没有什么补充。然而,希望这应该更容易阅读,速度更快,并且它不适合评论,因此我在回答中发布了这篇文章。老实说,我对
中最短的
不满意。

您可以使用后缀树模块,它是基于泛化后缀树的ANSI C实现的包装器。该模块易于处理


请看:

我更喜欢这一点,因为我觉得它更具可读性和直观性:

def is_substr(find, data):
  """
  inputs a substring to find, returns True only 
  if found for each data in data list
  """

  if len(find) < 1 or len(data) < 1:
    return False # expected input DNE

  is_found = True # and-ing to False anywhere in data will return False
  for i in data:
    print "Looking for substring %s in %s..." % (find, i)
    is_found = is_found and find in i
  return is_found
def是_substr(查找,数据):
"""
输入要查找的子字符串,仅返回True
如果为数据列表中的每个数据找到
"""
如果len(find)<1或len(data)<1:
返回False#预期输入DNE
is_found=True#如果在数据中的任何位置将其设置为False,则返回False
对于数据中的i:
打印“查找%s中的子字符串%s…”%(查找,i)
is_found=is_found and find in i
已找到退货

如果有人正在寻找一个通用版本,该版本还可以获取任意对象序列的列表:

def get_longest_common_subseq(data):
    substr = []
    if len(data) > 1 and len(data[0]) > 0:
        for i in range(len(data[0])):
            for j in range(len(data[0])-i+1):
                if j > len(substr) and is_subseq_of_any(data[0][i:i+j], data):
                    substr = data[0][i:i+j]
    return substr

def is_subseq_of_any(find, data):
    if len(data) < 1 and len(find) < 1:
        return False
    for i in range(len(data)):
        if not is_subseq(find, data[i]):
            return False
    return True

# Will also return True if possible_subseq == seq.
def is_subseq(possible_subseq, seq):
    if len(possible_subseq) > len(seq):
        return False
    def get_length_n_slices(n):
        for i in xrange(len(seq) + 1 - n):
            yield seq[i:i+n]
    for slyce in get_length_n_slices(len(possible_subseq)):
        if slyce == possible_subseq:
            return True
    return False

print get_longest_common_subseq([[1, 2, 3, 4, 5], [2, 3, 4, 5, 6]])

print get_longest_common_subseq(['Oh, hello, my friend.',
                                     'I prefer Jelly Belly beans.',
                                     'When hell freezes over!'])
def get_longest_common_subseq(数据):
substr=[]
如果len(数据)>1且len(数据[0])>0:
对于范围内的i(len(数据[0]):
对于范围内的j(len(数据[0])-i+1):
如果j>len(substr)并且是任意(数据[0][i:i+j],数据)的子对象:
substr=数据[0][i:i+j]
返回子序列
def是任何(查找、数据)的子项:
如果len(数据)<1且len(查找)<1:
返回错误
对于范围内的i(len(数据)):
如果不是,则为第Q子部分(查找,数据[i]):
返回错误
返回真值
#如果可能,也将返回True\u subseq==seq。
def为子部分(可能的子部分,顺序):
如果len(可能)>len(序号):
返回错误
def get_长度_n_切片(n):
对于x范围内的i(len(seq)+1-n):
收益率序列[i:i+n]
对于获取长度n个切片中的slyce(len(可能的子集)):
如果slyce==可能的子问题:
返回真值
返回错误
打印获取最长公共子Q([[1,2,3,4,5],[2,3,4,5,6])
打印获取最长的公共子标题([“哦,你好,我的朋友。”),
“我更喜欢果冻肚豆。”,
“当地狱结冰时!”)

这可以缩短时间:

def long_substr(数据):
substrs=lambda x:{x[i:i+j]表示范围内的i(len(x))表示范围内的j(len(x)-i+1)}
s=SUBSTR(数据[0])
对于数据[1:]中的val:
s、 交叉口_更新(SUBSTR(val))
返回最大值(s,key=len)
集合(可能)被实现为散列映射,这使得这有点低效。如果您(1)实现一个集数据类型作为一个trie,并且(2)只将后缀存储在trie中,然后强制每个节点成为一个端点(这相当于添加所有子字符串),那么从理论上讲,我猜这个婴儿相当节省内存,特别是因为尝试的交叉非常容易


然而,这是短暂的,过早的优化是浪费大量时间的根源。

我的回答很慢,但很容易理解。处理一个包含100个1KB字符串的文件大约需要两秒钟,如果有多个子字符串,则返回任意一个最长的子字符串

ls = list()
ls.sort(key=len)
s1 = ls.pop(0)
maxl = len(s1)
#1创建按长度向后排序的所有子字符串的列表。因此,我们不必检查整个列表

subs = [s1[i:j] for i in range(maxl) for j in range(maxl,i,-1)]
subs.sort(key=len, reverse=True)
    
#2检查一个子串是否下一个最短,然后下一个等。如果不在任何下一个最短的字符串中,则中断循环,这是不常见的。如果它通过了所有检查,那么默认情况下它是最长的一个,打破循环

def isasub(subs, ls):
    for sub in subs:
        for st in ls:
            if sub not in st:
                break 
        else:
            return sub
            break
print('the longest common substring is: ',isasub(subs,ls))

Caveman解决方案,它将根据作为列表传递的子字符串长度,为您提供一个具有字符串中最频繁子字符串的数据帧:

import pandas as pd

lista = ['How much wood would a woodchuck',' chuck if a woodchuck could chuck wood?']

string = ''
for i in lista:
    string = string + ' ' + str(i)

string = string.lower()

characters_you_would_like_to_remove_from_string = [' ','-','_']

for i in charecters_you_would_like_to_remove_from_string:
    string = string.replace(i,'')

substring_length_you_want_to_check = [3,4,5,6,7,8]

results_list = []

for string_length in substring_length_you_want_to_check:
    for i in range(len(string)):
        checking_str = string[i:i+string_length]
        if len(checking_str) == string_length:
            number_of_times_appears = (len(string) - len(string.replace(checking_str,'')))/string_length
            results_list = results_list+[[checking_str,number_of_times_appears]]


df = pd.DataFrame(data=results_list,columns=['string','freq'])

df['freq'] = df['freq'].astype('int64')

df = df.drop_duplicates()


df = df.sort_values(by='freq',ascending=False)

display(df[:10])

结果是:

    string  freq
78    huck     4
63    wood     4
77    chuc     4
132  chuck     4
8      ood     4
7      woo     4
21     chu     4
23     uck     4
22     huc     4
20     dch     3

您的算法具有O(n1*n1*(n1+…+nK))时间复杂度,但使用后缀树可以将其降低为Θ(n1+…+nK)
是Θcommon_substr=lambda s,strings:all(x中的s表示字符串中的x)
对于具有单个元素的列表,它返回空字符串。在这种情况下,返回元素本身可能更有意义。应该提到的是,如果有多个相同长度的匹配序列,则只查找第一个最长的公共子字符串,而不是全部。尝试例如,.[Los Angeles”,“Lossless']由@J.F.Sebastian提出的添加是否提高了时间复杂度?请检查
shortest\u of
的“functional”版本。如果最长公共子字符串位于引用字符串的末尾,则会遗漏其最后一个字符。可以通过将xrange(i+len(substr)+1,长度)中j的
替换为xrange(i+len(substr)+1,长度+1)中j的
来修复此问题。链接已断开,您没有进行任何描述或示例
def isasub(subs, ls):
    for sub in subs:
        for st in ls:
            if sub not in st:
                break 
        else:
            return sub
            break
print('the longest common substring is: ',isasub(subs,ls))
import pandas as pd

lista = ['How much wood would a woodchuck',' chuck if a woodchuck could chuck wood?']

string = ''
for i in lista:
    string = string + ' ' + str(i)

string = string.lower()

characters_you_would_like_to_remove_from_string = [' ','-','_']

for i in charecters_you_would_like_to_remove_from_string:
    string = string.replace(i,'')

substring_length_you_want_to_check = [3,4,5,6,7,8]

results_list = []

for string_length in substring_length_you_want_to_check:
    for i in range(len(string)):
        checking_str = string[i:i+string_length]
        if len(checking_str) == string_length:
            number_of_times_appears = (len(string) - len(string.replace(checking_str,'')))/string_length
            results_list = results_list+[[checking_str,number_of_times_appears]]


df = pd.DataFrame(data=results_list,columns=['string','freq'])

df['freq'] = df['freq'].astype('int64')

df = df.drop_duplicates()


df = df.sort_values(by='freq',ascending=False)

display(df[:10])

    string  freq
78    huck     4
63    wood     4
77    chuc     4
132  chuck     4
8      ood     4
7      woo     4
21     chu     4
23     uck     4
22     huc     4
20     dch     3