1.最长上升子序列
对于一个数字序列,请设计一个复杂度为O(nlogn)的算法,返回该序列的最长上升子序列的长度,这里的子序列定义为这样一个序列U1,U2...,其中Ui < Ui+1,且A[Ui] < A[Ui+1]。 给定一个数字序列A及序列的长度n,请返回最长上升子序列的长度。 测试样例: [2,1,4,3,1,5,6],7 返回:4
方法1:O(n2)
生成一个新的列表b,b和输入列表a对应长度。b[t]表示a以a[t]为结尾的上升子序列长度。
# -*- coding:utf-8 -*- class AscentSequence: def findLongest(self, A, n): # write code here b=[]#存放相应位置最长子序列的长度 for i in range(len(A)):#循环a temp=A[i] b.append(1)#b赋初值1 for j in range(i):#对i之前数遍历,找到以a[i]结尾的子序列长度 if(A[i]>A[j] and b[j]+1>b[i]): b[i]=b[j]+1 result=max(b) return result
方法2.O(nlogn)
生成一个新的列表b,b[t]表示长度为t+1的子序列的结尾数字。
在一列数中寻找一些数,这些数满足:任意两个数a[i]和a[j],若i 设dp[i]表示以i为结尾的最长递增子序列的长度,则状态转移方程为: dp[i] = max{dp[j]+1}, 1<=j
考虑两个数a[x]和a[y],x 按dp[t]=k来分类,只需保留dp[t]=k的所有a[t]中的最小值,设g[k]记录这个值,g[k]=min{a[t],dp[t]=k}。 这时注意到dp的两个特点(重点): 1. dp[k]在计算过程中单调不升; 2. dp数组是有序的,dp[1] 利用这两个性质,可以很方便的求解: (1).设当前已求出的最长上升子序列的长度为len(初始时为1),每次读入一个新元素x: (2).若x>dp[len],则直接加入到dp的末尾,且len++;(利用性质2) 否则,在dp中二分查找,找到第一个比x小的数dp[k],并dp[k+1]=x,在这里x<=dp[k+1]一定成立(性质1,2)。 参考: http://blog.csdn.net/zhangyx_xyz/article/details/50949957 https://www.nowcoder.com/test/question/done?tid=10236396&qid=25104#summary 分析: 首先区别一下子串与子序列,子串要求是连续的而子序列不要求连续。例如ABCDEG字符串, ABC为子串,ADG为子序列。 设有两个字符串数组 str1【N】和str2【M】,构建二维数组DP【N】【M】,其中DP【i】【j】的含义:字符串str1【0……i】与str2【0……j】的公共子序列的长度。 我们来分析DP【i】【j】的来源, 1、首先DP【i】【j】的值不会小于DP【i-1】【j-1】,如果str1【i】==str2【j】,则DP【i】【j】 = DP【i-1】【j-1】+1。 2、如果str1【i】!= str2【j】,DP的来源于max( DP【i】【j-1】,DP【i-1】【j】), 此时不用考虑DP【i-1】【j-1】,因为: DP【i】【j-1】>=DP【i-1】【j-1】, DP【i-1】【j】>=DP【i-1】【j-1】 ( 没看明白可以这样考虑: str1【0……i】与str2【0……j-1】 的公共子序列肯定不会比str1【0……i-1】与str2【0……j-1】的公共子序列小, 同理str1【0……i-1】与str2【0……j】的公共子序列肯定不会比str1【0……i-1】与str2【0……j-1】的公共子序列小。) 所以选取这两个最大一个即可。 (实际上分了三种情况讨论。。。) 明白DP【i】【j】的含义,以及DP【i】【j】的推导后,我们再来看如何构造DP【N】【M】数组。 1、首先构造第一行, DP【0】【0……M-1】代表字符串str1【0……0】与str2【0……M-1】的最长公共子序列, 可以看出这一行的值,不为1就为0, 且当DP【0】【K】为1时(含义:str1【0】 ==str2【K】&&str1【0】!=str2【0……K-1】) DP【0】【K+1……M-1】均为1。 2、构造第一列, DP【0……N-1】【0】代表str1【0……N-1】与str2【0……0】的最长公共子序列, 可以看出这一列的值同第一行一样,不为1就为0, 且当DP【K】【0】为1时(含义:str1【K】 ==str2【0】&&str1【0……K-1】!=str2【0】) DP【K+1……M-1】【0】均为1。 3、知道了第一行和第一列的值,可以根据求得其他所有矩阵中的值。 如果题目还让我们输出这个最长公共子序列呢? 构造DP【N】【M】之后,就可以根据DP【N】【M】来求得最长公共子序列。 1、首先从右下角开始,DP【N-1】【M-1】,如果str1【N-1】 == str2【M-1】 则说明str1【N-1】一定是最长公共子序列中的一个数,得记录下来 2、如果str1【N-1】 != str2【M-1】,则不用记录str1【N-1】或str2【M-1】,这时需要向上(DP【i-1】【j】)移动或者向左 (DP【i】【j-1】)移动,看这两个值哪个大,谁大往谁的方向移。(可以这样理解,既然我们这个点str1【N-1】与str2【M-1】不同, 但是此时最大递增子序列的长度应该是不变的,所以应该向DP【】【】值不变的方向移动,回想起我们在创建DP【】【】数组时, 当str1【N-1】 != str2【M-1】,DP【N-1】【M-1】的来源是左边和上边其中的较大值。所以应该向左边和上边中的较大值移动) 3、每到str1【K1】 == str2【K2】 记录下str1【k1】,不相等时,向左和上的较大值移动,直到DP【K1】【k2】==0终止。 参考: http://blog.csdn.net/qq_27161673/article/details/52674898 https://www.nowcoder.com/test/question/done?tid=10236396&qid=25105#summary 下面是字符串21232523311324和字符串312123223445的匹配矩阵,前者为X方向的,后者为Y方向的。不难找到,红色部分是最长的匹配子串。通过查找位置我们得到最长的匹配子串为:21232 0 0 0 1 0 0 0 1 1 0 0 1 0 0 0 但是在0和1的矩阵中找最长的1对角线序列又要花去一定的时间。通过改进矩阵的生成方式和设置标记变量,可以省去这部分时间。 0 0 0 1 0 0 0 1 1 0 0 1 0 0 0 当字符匹配的时候,我们并不是简单的给相应元素赋上1,而是赋上其左上角元素的值加一。我们用两个标记变量来标记矩阵中值最大的元素的位置,在矩阵生成的过程中来判断当前生成的元素的值是不是最大的,据此来改变标记 量的值,那么到矩阵完成的时候,最长匹配子串的位置和长度就已经出来了。 分析: 同样,我们令a=len(A),b=len(B),我们可以申请一个dp——(a+1)*(b+1)的列表。用来保存我们的中间结果。 dp=[i][j]表示A[i-1]到B[j-1]的编辑距离; dp[0][0]=0; dp[i][0]表示A[i-1]到B[0]的编辑距离,要不停的删除; dp[0][j]表示A[0]到B[j-1]的编辑距离,要不停的添加; 如何求dp[i][j]?首先dp[i][j]表示的是将A[i-1]变成B[j-1]。三个途径。dp[i-1][j-1] dp[i][j-1] dp[i-1][j] 1.当A[i-1]==B[j-1]时,不用编辑,dp[i][j]=dp[i-1][j-1]; 2.当A[i-1]!=B[j-1]时,dp[i][j]=min(dp[i-1][j-1]+c2, dp[i][j-1]+c0,dp[i-1][j]+c1)。 下面我们解释第二种情况中min中的三个子编辑的来源。 要求dp[i][j],我们可以直接把A[i-1]修改成B[j-1],这里就是dp[i-1][j-1]+c2 ; 要求dp[i][j],我们可以把A[i-1]改成B[j-2],然后再添加一个B[j-1],这里A[i-1]改成B[j-2]的代价已经求出来了,就是dp[i][j-1],所以总的代价是dp[i][j-1]+c0; 要求dp[i][j],我们可以把A[i-2]改成B[j-1],然后再删除A[i],因为我们的终极目标就是要得到B[j-1],dp[i-1][j]已经做到了,那么我们可以把多出来的元素A[i]删掉即可。 最后我们从这三个当中找一个最小的编辑代价即可。 注意:1.i和j分别对应谁,这个比较容易出错;2.不要把min写成max 分析: 假设str1的长度为N,str2的长度为M,生成(N+1)*(M+1)的dp矩阵,为什么是N+1,M+1,因为我们需要在字符串的开头添加一个空字符的特殊情况,dp[i][j]的值代表aim[0…i+j-1]能否被str1[0…i-1]和str2[0…j-1]交错组成,dp[i][j]的值计算如下: 矩阵的第一行表示能否只用str2[0…j-1]交错组成aim[0…j-1],如果str2[0…j-1]等于aim[0…j-1],则令dp[0][j] = True,否则为False 矩阵的第一列同上,如果str1[0…i-1]等于aim[0…i-1],则令dp[i][0] = True,否则为False 矩阵的其余位置由以下情况决定: 1)dp[i-1][j]代表aim[0…i+j-2]能否被str1[0…i-2]和str2[0…j-1]交错组成,如果可以,那么如果再有str1[i-1]等于aim[i+j-1],说明str1[i-1]又可以作为交错组成aim[0…i+j-1]的最后一个字符。令dp[i][j] = True 2)dp[i][j-1]代表aim[0…i+j-2]能否被str2[0…j-2]和str1[0…i-1]交错组成,如果可以,那么如果再有str2[j-1]等于aim[i+j-1],说明str2[j-1]又可以作为交错组成aim[0…i+j-1]的最后一个字符。令dp[i][j] = True 3)如果上述情况不满足,令dp[i][j] = False 注意:dp矩阵只是存储过程,并非真正的步骤,字体中粉色1)的话,dp[i][j]就是判断str1[i-1]和str2[j-1]能否表示str3[i+j-1],如果dp[i-1][j]代表aim[0…i+j-2]能否被str1[0…i-2]和str2[0…j-1]交错组成,所以问题的关键是str1[i-1]是不是和str3[i+j-1]相等。2)同理。 千万要理解dp矩阵的含义。 方法2:用递归 参考: http://m.blog.csdn.net/qq_34342154/article/details/77151959 http://www.cnblogs.com/sunp823/p/5601406.html # -*- coding:utf-8 -*-
class AscentSequence:
def findLongest(self, A, n):
# write code here
b=[]#b位置上存储是下标位置-1(长度)的结尾数字
b.append(A[0])#先把第一个元素放进来
for i in range(1,len(A)):
if(A[i]>b[-1]):#如果A大于b的最后一个数字,b直接追加
b.append(A[i])
else:
for j in range(len(b)):
if (A[i]#A中元素与b中元素比较,所以是nLogn的复杂度
b[j]=A[i]#相同长度的子序列,选择最小的那个
break#一定要跳出循环
return len(b)
2.最长公共子序列长度
对于两个字符串,请设计一个高效算法,求他们的最长公共子序列的长度,这里的最长公共子序列定义为有两个序列U1,U2,U3...Un和V1,V2,V3...Vn,其中Ui<Ui+1,Vi<Vi+1。且A[Ui] == B[Vi]。
给定两个字符串A和B,同时给定两个串的长度n和m,请返回最长公共子序列的长度。保证两串长度均小于等于300。
测试样例:
"1A2C3D4B56",10,"B1D23CA45B6A",12
返回:6
# -*- coding:utf-8 -*-
class LCS:
def findLCS(self, A, n, B, m):
# write code here
#这里一定要注意的是,列表里边的m和n的顺序,一定要和下边for循环合拍,容易错
dp=[[0 for i in range(m+1)]for j in range(n+1)]
for i in range(n):
for j in range(m):
if A[i]==B[j]:
dp[i+1][j+1]=dp[i][j]+1
else:
dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j])
return dp[n][m]
3.最长公共子串
对于两个字符串,请设计一个时间复杂度为O(m*n)的算法(这里的m和n为两串的长度),求出两串的最长公共子串的长度。这里的最长公共子串的定义为两个序列U1,U2,..Un和V1,V2,...Vn,
其中Ui + 1 == Ui+1,Vi + 1 == Vi+1,同时Ui == Vi。
给定两个字符串A和B,同时给定两串的长度n和m。
测试样例:
"1AB2345CD",9,"12345EF",7
返回:4
0 1 0 0 0 0 0 0 0 1 1 0 0 0 0
1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
0 1 0 0 0 0 0 0 0 1 1 0 0 0 0
1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
0 0 0 1 0 0 0 1 1 0 0 1 0 0 0
1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
0 0 0 1 0 0 0 1 1 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0 0 2 1 0 0 0 0
1 0 2 0 1 0 1 0 0 0 0 0 1 0 0
0 2 0 0 0 0 0 0 0 1 1 0 0 0 0
1 0 3 0 1 0 1 0 0 0 0 0 1 0 0
0 0 0 4 0 0 0 2 1 0 0 1 0 0 0
1 0 1 0 5 0 1 0 0 0 0 0 2 0 0
1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
0 0 0 2 0 0 0 2 1 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0# -*- coding:utf-8 -*-
class LongestSubstring:
def findLongest(self, A, n, B, m):
# write code here
dp=[[0 for i in range(m+1)]for j in range(n+1)]
result=0
for i in range(n):
for j in range(m):
if A[i]==B[j]:
dp[i+1][j+1]=dp[i][j]+1
result=max(result,dp[i+1][j+1])
return result
4.最小编辑代价
对于两个字符串A和B,我们需要进行插入、删除和修改操作将A串变为B串,定义c0,c1,c2分别为三种操作的代价,请设计一个高效算法,求出将A串变为B串所需要的最少代价。
给定两个字符串A和B,及它们的长度和三种操作代价,请返回将A串变为B串所需要的最小代价。保证两串长度均小于等于300,且三种代价值均小于等于100。
测试样例:
"abc",3,"adc",3,5,3,100
返回:8
# -*- coding:utf-8 -*-
class MinCost:
def findMinCost(self, A, n, B, m, c0, c1, c2):
# write code here
dp=[[0 for i in range(m+1)] for j in range(n+1)]
for i in range(1,m+1):
dp[0][i]=dp[0][i-1]+c0
for j in range(1,n+1):
dp[j][0]=dp[j-1][0]+c1
for j in range(1,n+1):
for i in range(1,m+1):
if A[j-1]==B[i-1]:
dp[j][i]=dp[j-1][i-1]
else:
dp[j][i]=min(dp[j-1][i-1]+c2,dp[j-1][i]+c1,dp[j][i-1]+c0)
return dp[-1][-1]
5.字符串交错组成
对于三个字符串A,B,C。我们称C由A和B交错组成当且仅当C包含且仅包含A,B中所有字符,且对应的顺序不改变。请编写一个高效算法,判断C串是否由A和B交错组成。
给定三个字符串A,B和C,及他们的长度。请返回一个bool值,代表C是否由A和B交错组成。保证三个串的长度均小于等于100。
测试样例:
"ABC",3,"12C",3,"A12BCC",6
返回:true
# -*- coding:utf-8 -*-
class Mixture:
def chkMixture(self, A, n, B, m, C, v):
# write code here
#if A == None or B == None or C == None or len(A)+len(B) != len(C):
#return False
dp = [[False for i in range(len(B)+1)] for j in range(len(A)+1)]
dp[0][0] = True
for i in range(1, len(A)+1):
if A[i-1] != C[i-1]:
break
dp[i][0] = True
for j in range(1, len(B)+1):
if B[j-1] != C[j-1]:
break
dp[0][j] = True
for i in range(1, len(A)+1):
for j in range(1, len(B)+1):
if (dp[i-1][j] == True and A[i-1] == C[i+j-1]) or (dp[i][j-1] == True and B[j-1] == C[i+j-1]):
dp[i][j] = True
return dp[-1][-1]
class Mixture:
def chkMixture(self, A, n, B, m, C, v):
# write code here
if(v == 0):
return true
if(n == 0):
return B == C
if(m == 0):
return A == C
if(A[0]==C[0] and B[0]!=C[0]):
return self.chkMixture(A[1:],n-1,B,m,C[1:],v-1)
elif(B[0]==C[0] and A[0]!=C[0]):
return self.chkMixture(A,n,B[1:],m-1,C[1:],v-1)
elif(B[0]==C[0] and A[0]==C[0]):
return self.chkMixture(A[1:],n-1,B,m,C[1:],v-1) or self.chkMixture(A,n,B[1:],m-1,C[1:],v-1)
else:
return False