簡體   English   中英

來自兩個以上字符串的最長公共子字符串

[英]Longest common substring from more than two strings

我正在尋找一個 Python 庫,用於從一組 strings 中找到最長的公共子字符串 有兩種方法可以解決這個問題:

  • 使用后綴樹
  • 使用動態規划。

實現的方法並不重要。 重要的是它可以用於一組字符串(不僅僅是兩個字符串)。

這些配對函數將在任意字符串數組中找到最長的公共字符串:

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!'])

毫無疑問,算法可以改進,而且我對 Python 的接觸不多,所以也許它在語法上也可以更有效,但它應該可以完成這項工作。

編輯:內聯第二個 is_substr 函數,如 JF Sebastian 所示。 用法保持不變。 注意:算法沒有變化。

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 all(data[0][i:i+j] in x for x in data):
                    substr = data[0][i:i+j]
    return substr

希望這可以幫助,

傑森。

這可以做得更短:

def long_substr(data):
  substrs = lambda x: {x[i:i+j] for i in range(len(x)) for j in range(len(x) - i + 1)}
  s = substrs(data[0])
  for val in data[1:]:
    s.intersection_update(substrs(val))
  return max(s, key=len)

集合(可能)被實現為哈希映射,這使得這有點低效。 如果您 (1) 將 set 數據類型實現為 trie 並且 (2) 僅將后綴存儲在 trie 中,然后強制每個節點成為端點(這相當於添加所有子字符串),那么理論上我猜這個嬰兒的記憶效率很高,特別是因為嘗試的交叉點非常容易。

然而,這是短暫的,過早的優化是大量浪費時間的根源。

我更喜歡is_substr ,因為我發現它更具可讀性和直觀性:

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 common_prefix(strings):
    """ Find the longest string that is a prefix of all the strings.
    """
    if not strings:
        return ''
    prefix = strings[0]
    for s in strings:
        if len(s) < len(prefix):
            prefix = prefix[:len(s)]
        if not prefix:
            return ''
        for i in range(len(prefix)):
            if prefix[i] != s[i]:
                prefix = prefix[:i]
                break
    return prefix

來自http://bitbucket.org/ned/cog/src/tip/cogapp/whiteutils.py

# this does not increase asymptotical complexity
# but can still waste more time than it saves. TODO: profile
def shortest_of(strings):
    return min(strings, key=len)

def long_substr(strings):
    substr = ""
    if not strings:
        return substr
    reference = shortest_of(strings) #strings[0]
    length = len(reference)
    #find a suitable slice i:j
    for i in xrange(length):
        #only consider strings long at least len(substr) + 1
        for j in xrange(i + len(substr) + 1, length + 1):
            candidate = reference[i:j]  # ↓ is the slice recalculated every time?
            if all(candidate in text for text in strings):
                substr = candidate
    return substr

免責聲明這對 jtjacques 的回答幾乎沒有增加。 但是,希望這應該更具可讀性更快,並且它不適合評論,因此我將其發布在答案中。 我不滿意約shortest_of ,是誠實的。

如果有人正在尋找也可以采用任意對象序列列表的通用版本:

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!'])

我的回答很慢,但很容易理解。 處理一個包含 100 個 1 kb 字符串的文件大約需要兩秒鍾,如果有多個,則返回任何一個最長的子字符串

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

在我的機器上添加一個“中斷”可以顯着加快 jtjacques 的回答速度(對於 16K 文件,大約為 1000 倍):

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(substr)+1, len(data[0])-i+1):
                if all(data[0][i:i+j] in x for x in data[1:]):
                    substr = data[0][i:i+j]
                else:
                    break
    return substr

您可以使用 SuffixTree 模塊,該模塊是基於通用后綴樹的 ANSI C 實現的包裝器。 該模塊易於處理......

看一看:這里

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM