微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

查找两个字符串之间的公共有序字符

如何解决查找两个字符串之间的公共有序字符

给出两个字符串,找到两个字符串之间的公用字符,它们从左到右的顺序相同。

示例1

string_1 = 'hcarry'
string_2 = 'sallyc'

Output - 'ay'

示例2

string_1 = 'jenny'
string_2 = 'ydjeu'

Output - 'je'

示例1的说明-

string_1string_2间的公用字符为c,a,y。但是,由于cay中的string_1 之前和ay中的string_2之后,因此我们不会在输出中考虑字符c。两个字符串之间的公共字符顺序必须保持并且必须相同。

示例2的说明-

string_1string_2间的公共字符是j,e,y。但是,由于yje中的string_2 之前和je中的string_1之后,因此我们不会在输出中考虑字符y。两个字符串之间的公共字符顺序必须保持并且必须相同。

我的方法-

  1. 找到字符串之间的公共字符,然后将其存储在每个单独字符串的另一个变量中。

Example - 

string_1 = 'hcarry'
string_2 = 'sallyc'

Common_characters = c,a,y

string_1_com = cay
string_2_com = ayc

我使用sorted,counter,enumerate函数在Python中获取string_1_com and string_2_com

  1. 现在找到string_1_com and string_2_com 之间最长的公共子序列。您将得到输出作为结果。

这是蛮力解决方案。

什么是最佳解决方案?

解决方法

此算法在我的书中仅称为字符串匹配。它在O( mn )中运行,其中 m n 是单词长度。我猜想它也可以运行在整个单词上,最有效的选择取决于预期的常用字母数量以及排序和过滤的方式。我将针对常见的字母字符串进行解释,因为这样比较容易。

这个想法是,您查看(m + 1) * (n + 1)个节点的有向无环图。通过此图的每条路径(从左上到右下)都表示一种匹配单词的独特方式。我们要匹配字符串,并在单词中附加空格(-),以使它们与最多的普通字母对齐。例如,cayayc的结束状态为

cay-
-ayc

每个节点都存储了它代表的部分匹配的最大匹配数,并且在算法结束时,结束节点将为我们提供最大匹配数。

我们从左上角开始,那里什么都没有匹配,因此这里有0个匹配字母(分数0)。

    c a y
  0 . . .
a . . . .
y . . . .
c . . . .

我们将遍历此图,并使用先前节点中的数据为每个节点计算匹配字母的最大数量。

节点从左->右,上->下和对角线左上->右下连接。

  • 向右移动表示消耗cay中的一个字母,并将我们到达的字母与-中插入的ayc相匹配。
  • 向下移动代表相反的情况(从ayc开始消费,然后将-插入cay)。
  • 对角移动表示每个单词消耗一个字母并与之匹配。

查看起始节点右侧的第一个节点,它表示匹配

c
-

(显然)只能从起始节点到达此节点。

第一行和第一列中的所有节点都将为0,因为它们都表示匹配的一个或多个字母,它们具有相等数量的-

我们得到了图

    c a y
  0 0 0 0
a 0 . . .
y 0 . . .
c 0 . . .

那是设置,现在有趣的部分开始了。

查看第一个未评估的节点,该节点表示将子串ca匹配,我们想决定如何以最多的匹配字母到达那里。

  • 替代方案1:我们可以从左侧的节点到达那里。左侧的节点代表匹配项
-
a

因此,通过选择此路径到达我们的当前节点,我们到达

-c
a-

c-进行匹配不会给我们带来正确的匹配,因此该路径的得分是0(从最后一个节点获取)加上0(匹配项c/-的得分) )。因此,该路径的0 + 0 = 0。

  • 替代2:我们可以从上方到达此节点,此路径表示从此处移动
c   ->    c-
-         -a

这也给我们0分。分数是0。

  • 替代3:我们可以从左上方到达此节点。从开始节点(什么都没有)到每个字母都消耗一个字符。那是匹配的
c
a

由于ca是不同的字母,因此该路径也得到0 + 0 = 0。

    c a y
  0 0 0 0
a 0 0 . .
y 0 . . .
c 0 . . .

但是对于下一个节点,它看起来更好。我们仍然有三种选择。 选项1和2总是给我们0分,因为它们总是表示与-匹配的字母,因此这些路径将给我们得分0。让我们继续选择3。

对于我们当前的节点,对角移动意味着从

c   ->   ca
-        -a

这是一场比赛!

这意味着有一条通往该节点的路径,该路径的得分为1。我们丢掉0并保存1。

    c a y
  0 0 0 0
a 0 0 1 .
y 0 . . .
c 0 . . .

对于该行的最后一个节点,我们看一下三种选择,并意识到我们不会获得任何新的点(新的匹配项),但是我们可以使用先前的1点路径到达该节点:

ca   ->   cay
-a        -a-

因此,该节点的得分也为1。

对所有节点执行此操作,我们得到以下完整图

    c a y
  0 0 0 0
a 0 0 1 1
y 0 0 1 2
c 0 1 1 2

得分唯一增加的地方

c   ->   ca   |   ca   ->   cay   |   -   ->   -c
-        -a   |   -a        -ay   |   y        yc

因此,结束节点告诉我们最大匹配为2个字母。 由于在您的情况下,您希望知道得分为2的最长路径,因此还需要针对每个节点跟踪所采用的路径。

此图很容易实现为矩阵(或数组数组)。

我建议您将tuple作为元素使用一个score元素和一个path元素,并且在path元素中只存储对齐字母,然后将最终矩阵将是

    c      a        y
  0 0      0        0
a 0 0      (1,a)   (1,a)
y 0 0      (1,a)   (2,ay)
c 0 (1,c) (1,a/c) (2,ay)

在一个地方我注意到a/c,这是因为字符串caayc具有两个不同的最大长度子序列。您需要决定在这种情况下该怎么做,或者只选择一个,或者同时保存两个。

编辑:

这是此解决方案的实现。

def longest_common(string_1,string_2):
    len_1 = len(string_1)
    len_2 = len(string_2)
    
    m = [[(0,"") for _ in range(len_1 + 1)] for _ in range(len_2 + 1)] # intitate matrix
    
    for row in range(1,len_2+1):
        for col in range(1,len_1+1):
            diag = 0
            match = ""
            if string_1[col-1] == string_2[row-1]: # score increase with one if letters match in diagonal move
                diag = 1
                match = string_1[col - 1]
            # find best alternative
            if m[row][col-1][0] >= m[row-1][col][0] and m[row][col-1][0] >= m[row-1][col-1][0]+diag:
                m[row][col] = m[row][col-1] # path from left is best
            elif m[row-1][col][0] >= m[row-1][col-1][0]+diag:
                m[row][col] = m[row-1][col] # path from above is best
            else:
                m[row][col] = (m[row-1][col-1][0]+diag,m[row-1][col-1][1]+match) # path diagonally is best

    return m[len_2][len_1][1]
>>> print(longest_common("hcarry","sallyc"))
ay
>>> print(longest_common("cay","ayc"))
ay
>>> m
[[(0,''),(0,'')],[(0,(1,'a'),'a')],(2,'ay')],'c'),'ay')]]
,

这是一个简单的基于动态编程的问题解决方案:

def lcs(X,Y): 
    m,n = len(X),len(Y)
    L = [[0 for x in xrange(n+1)] for x in xrange(m+1)] 
  
    # using a 2D Matrix for dynamic programming
    # L[i][j] stores length of longest common string for X[0:i] and Y[0:j]
    for i in range(m+1): 
        for j in range(n+1): 
            if i == 0 or j == 0: 
                L[i][j] = 0
            elif X[i-1] == Y[j-1]: 
                L[i][j] = L[i-1][j-1] + 1
            else: 
                L[i][j] = max(L[i-1][j],L[i][j-1]) 
  
    # Following code is used to find the common string 
    index = L[m][n] 
  
    # Create a character array to store the lcs string 
    lcs = [""] * (index+1) 
    lcs[index] = "" 
  
    # Start from the right-most-bottom-most corner and 
    # one by one store characters in lcs[] 
    i = m 
    j = n 
    while i > 0 and j > 0: 
  
        # If current character in X[] and Y are same,then 
        # current character is part of LCS 
        if X[i-1] == Y[j-1]: 
            lcs[index-1] = X[i-1] 
            i-=1
            j-=1
            index-=1
  
        # If not same,then find the larger of two and 
        # go in the direction of larger value 
        elif L[i-1][j] > L[i][j-1]: 
            i-=1
        else: 
            j-=1
  
    print ("".join(lcs))
,

但是..您已经知道术语“最长公共子序列”,并且可以找到有关动态编程算法的大量描述。
Wiki link

伪代码

function LCSLength(X[1..m],Y[1..n])
    C = array(0..m,0..n)
    for i := 0..m
        C[i,0] = 0
    for j := 0..n
        C[0,j] = 0
    for i := 1..m
        for j := 1..n
            if X[i] = Y[j] //i-1 and j-1 if reading X & Y from zero
                C[i,j] := C[i-1,j-1] + 1
            else
                C[i,j] := max(C[i,j-1],C[i-1,j])
    return C[m,n]

function backtrack(C[0..m,0..n],X[1..m],Y[1..n],i,j)
    if i = 0 or j = 0
        return ""
    if  X[i] = Y[j]
        return backtrack(C,X,Y,i-1,j-1) + X[i]
    if C[i,j-1] > C[i-1,j]
        return backtrack(C,j-1)
    return backtrack(C,j)
,

简单得多的解决方案-----谢谢!

def f(s,s1):
 cc = list(set(s) & set(s1))
 ns = ''.join([S for S in s if S in cc])
 ns1 = ''.join([S for S in s1 if S in cc])
 found = []
 b = ns[0]
 for e in ns[1:]:
    cs = b+e
    if cs in ns1:
        found.append(cs)
    b = e
 return found

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。