[英]Find the common ordered characters between two strings
给定两个字符串,找出两个字符串之间从左到右顺序相同的公共字符。
示例 1
string_1 = 'hcarry'
string_2 = 'sallyc'
Output - 'ay'
示例 2
string_1 = 'jenny'
string_2 = 'ydjeu'
Output - 'je'
示例 1 的说明 -
string_1
和string_2
之间的公共字符是 c,a,y。 但由于c
到来之前ay
在string_1
之后ay
在string_2
,我们不会考虑字符c
输出。 两个字符串之间的公共字符的顺序必须保持并且必须相同。
示例 2 的说明 -
string_1
和string_2
之间的公共字符是 j,e,y。 但由于y
到来之前je
在string_2
后je
在string_1
,我们不会考虑字符y
输出。 两个字符串之间的公共字符的顺序必须保持并且必须相同。
我的方法——
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
。
string_1_com and string_2_com
之间的最长公共子序列。 您将获得输出作为结果。这是蛮力解决方案。
对此的最佳解决方案是什么?
这个算法在我的书中被称为字符串匹配。 它在 O( mn ) 中运行,其中m和n是字长。 我想它也可以在完整的单词上运行,最有效的将取决于预期的常见字母数量以及排序和过滤的执行方式。 我将为常见的字母字符串解释它,因为这更容易。
这个想法是你看一个(m+1) * (n+1)节点的有向无环图。 通过该图的每条路径(从左上角到右下角)都代表了一种匹配单词的独特方式。 我们要匹配字符串,并在单词中另外放入空格 ( -
),以便它们与最多的常见字母对齐。 例如cay
和ayc
的最终状态是
cay-
-ayc
每个节点存储它所代表的部分匹配的最高匹配数,并且在算法结束时,端节点将为我们提供最高匹配数。
我们从左上角开始,没有与没有匹配的地方,因此我们有 0 个匹配的字母(得分 0)。
c a y
0 . . .
a . . . .
y . . . .
c . . . .
我们将遍历此图,并通过使用来自先前节点的数据,为每个节点计算匹配字母的最高数量。
节点连接左->右、上->下和对角线左-上->右-下。
cay
消耗一个字母并将我们到达的字母与 a -
插入ayc
。ayc
消耗并插入-
到cay
)。查看起始节点右侧的第一个节点,它表示匹配
c
-
并且这个节点(显然)只能从起始节点到达。
第一行和第一列中的所有节点都将为 0,因为它们都表示匹配一个或多个具有相同数量的字母-
。
我们得到图形
c a y
0 0 0 0
a 0 . . .
y 0 . . .
c 0 . . .
这就是设置,现在有趣的部分开始了。
查看第一个未评估的节点,它表示将子字符串c
与a
匹配,我们想决定如何使用最多匹配的字母到达那里。
-
a
所以通过选择这条路径到达我们当前的节点,我们到达
-c
a-
将c
与-
匹配给我们没有正确的匹配,因此这条路径的分数是 0(取自最后一个节点)加上 0(匹配c/-
分数)。 所以 0 + 0 = 0 对于这条路径。
c -> c-
- -a
这也给了我们 0 加分。 这方面的分数是 0。
c
a
由于c
和a
是不同的字母,因此这条路径也得到 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 的最长路径,因此您还需要为每个节点跟踪所采用的路径。
该图很容易实现为矩阵(或数组数组)。
我建议你作为元素使用一个带有一个score
元素和一个path
元素的tuple
,在 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
,这是因为字符串ca
和ayc
有两个不同的最大长度子序列。 您需要决定在这些情况下该怎么做,要么选择一个,要么两个都保存。
编辑:
这是此解决方案的实现。
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, ''), (0, '')],
[(0, ''), (0, ''), (1, 'a'), (1, 'a')],
[(0, ''), (0, ''), (1, 'a'), (2, 'ay')],
[(0, ''), (1, 'c'), (1, 'c'), (2, '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))
但是……您已经知道术语“最长公共子序列”,并且可以找到许多动态规划算法的描述。
维基链接
伪代码
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, X, Y, i, j-1)
return backtrack(C, X, Y, i-1, 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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.