Python 查找两个字符串之间的公共有序字符
给定两个字符串,查找两个字符串之间从左到右顺序相同的公共字符 示例1Python 查找两个字符串之间的公共有序字符,python,string,algorithm,data-structures,Python,String,Algorithm,Data Structures,给定两个字符串,查找两个字符串之间从左到右顺序相同的公共字符 示例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。但由于 > >代码>在代码> Strug1 之前,代码> Ay Strigy2,我们不会在输出中考虑字符代码>代码>
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。但由于string_1
和string_2
之间的常用字符是j、e、y。但是由于<>代码> y>代码>在<代码> > SGRIGIG2之前,<>代码> JE <代码> StRIGIG1,我们不会在输出中考虑字符<代码> y>代码>。两个字符串之间的公共字符顺序必须保持一致
我的方法-
排序、计数器、枚举
函数在Python中获得字符串\u 1\u com和字符串\u 2\u com
string\u 1\u com和string\u 2\u com
之间找到最长的公共子序列。您将得到结果的输出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]
这方面的最佳解决方案是什么?但是。。您已经知道术语“最长公共子序列”,并且可以找到许多关于动态规划算法的描述。
伪码
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)
但是。。您已经知道术语“最长公共子序列”,并且可以找到许多关于动态规划算法的描述。
伪码
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)
在我的书中,这个算法叫做字符串匹配。它在O(mn)中运行,其中m和n是字长。我想它也可以运行在完整的单词上,什么是最有效的将取决于预期的常用字母数量以及排序和过滤是如何执行的。我将解释它的普通字母字符串,因为这更容易 其思想是看(m+1)*(n+1)个节点的有向无环图。通过此图的每条路径(从左上到右下)表示匹配单词的唯一方式。我们希望匹配字符串,并在单词中添加空格(
-
),以便它们与最大数量的常用字母对齐。例如,cay
和ayc
的结束状态为
cay-
-ayc
每个节点为其表示的部分匹配存储最高数量的匹配,并且在算法结束时,结束节点将为我们提供最高数量的匹配
我们从左上角开始,在这里没有匹配的字母,所以这里有0个匹配的字母(分数0)
我们将遍历此图,并使用以前节点的数据计算每个节点的最大匹配字母数
节点连接为左->右、上->下和对角左上->右下
- 向右移动表示从
消费一个字母,并将我们到达的字母与插入cay
的ayc
匹配-
- 向下移动表示相反(从
消费,并将ayc
插入到-
)cay
- 对角移动表示从每个单词中抽取一个字母并匹配这些字母
c
-
这个节点(显然)只能从起始节点到达
第一行和第一列中的所有节点都将为0,因为它们都表示一个或多个字母与相同数量的-
匹配
我们得到了图表
c a y
0 0 0 0
a 0 . . .
y 0 . . .
c 0 . . .
这就是设置,现在有趣的部分开始了
查看第一个未计算的节点,它表示将子字符串c
与a
相匹配,我们想决定如何使用最多的匹配字母到达那里
- 备选方案1:我们可以从左边的节点到达那里。左侧的节点表示匹配的节点
-c
a-
将c
与-
匹配不会给出正确的匹配,因此此路径的分数为0(取自最后一个节点)加0(刚刚进行的匹配c/-
的分数)。因此,对于这个路径,0+0=0
- 备选方案2:我们可以从上面到达该节点,该路径表示从
- 备选方案3:我们可以从左上角到达该节点。这是从起始节点(完全没有)移动到从每个字母消耗一个字符。这就是匹配
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
因此,结束节点告诉我们最大匹配是
ca -> cay
-a -a-
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
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)
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 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