分享

Algorithm:C++/python语言实现之求旋转数组最小值、求零子数组、求最长公共子序列和最长公共子串、求LCS与字符串编辑距离

 处女座的程序猿 2021-09-28

Algorithm:C++/python语言实现之求旋转数组最小值、求零子数组、求最长公共子序列和最长公共子串、求LCS与字符串编辑距离


  一、求旋转数组最小值  

       假定一个排序数组以某个未知元素为支点做了旋转,如:原数组0 1 2 4 5 6 7旋转后得到4 5 6 7 0 1 2。请找出旋转后数组的最小值。假定数组中没有重复数字。

1、分析问题

        旋转之后的数组实际上可以划分成两个有序的子数组:前面子数组的大小,都大于后面子数组中的元素;4 5 6 7 0 1 2 注意到,实际上最小的元素就是两个子数组的分界线

2、解决思路

        用两个指针low、high分别指向数组的第一个元素和最后一个元素。如果是正常的排序数组(元素间不重复),第一个元素肯定小于最后一个元素。
       计算中间位置mid = (low+high)/2。
(1)、先考察A[mid]、A[low]关系
       若:A[mid]>A[low],则A[low,low+1….mid-1,mid]是递增序列,最小元素位于子数组A[mid+1,mid+2,…high]中。因此,做赋值low=mid+1。
       若:A[mid]<A[low] ,则A[low,low+1….mid-1,mid]不是递增序列,即:中间元素该子数组中,做赋值high=mid。
(2)、再考察A[mid]、A[high]关系
       对偶地,若考察A[mid]与A[high]的关系,能够得到相似的结论。

二、求零子数组

      求对于长度为N的数组A,求子数组的和接近0的子数组,要求时间复杂度O(NlogN)。

1、算法思路

      申请同样长度的空间sum[0…N-1],sum[i]是A的前i项和。
Trick:定义sum[-1] = 0
显然有:
算法:对sum[0…N-1]排序,然后计算sum相邻元素的差,最小值记为min1。
       min1:在A中任意取两个集合,各自元素的和求差的最小值
       因为sum[-1]=0,sum[0…N-1]的绝对值最小值记为min2。
       min2:A的前k个元素的和的绝对值的最小值
       min1和min2的更小者,即为所求。

2、要说明的两个问题

sum本身的计算和相邻元素差的计算,都是O(N),sum的排序是O(NlogN),因此,总时
间复杂度:O(NlogN)
强调:除了计算sum相邻元素的差的最小值,别忘了sum自身的最小值。一个对应A[i…j],一个对应A[0…j]

三、最长公共子串和最长公共子序列

1、最长公共子串(必须连续)—python实现

       Longest Common Substring最长公共子串。

def LCS_find_Substring(s1, s2): #求两个字符串最长公共子串
    '''
    用一个矩阵来记录两个字符串中所有位置的两个字符之间的匹配情况,
    若是匹配则为1,否则为0。
    然后求出对角线最长的1的序列,其对应的位置就是最长匹配子串的位置。
    '''
    matrix_2D=[  [0 for i in range(len(s2)+1)] 
                    for j in range(len(s1)+1)]    #定义全0矩阵,为方便后续计算,比字符串长度多了一列
#     print('生成(i+1)行、(j+1)列全0矩阵:',matrix_2D)
    
    length_max=0      #最长匹配的长度
    p_end=0           #最长匹配对应在s1中的最后一位
    for i in range(len(s1)):
        for j in range(len(s2)):
            if s1[i]==s2[j]:                   #第一个if判断,两字符串内元素对应相等时,矩阵内,再次相等元素处的索引累计+1
                matrix_2D[i+1][j+1]=matrix_2D[i][j]+1
            if matrix_2D[i+1][j+1]>length_max: #第二个if判断,将当前相等元素的个数,赋值给length_max
                length_max=matrix_2D[i+1][j+1]
                p_end=i+1                              #记录s1中连续相等情况下,最后的索引位置
        print(matrix_2D)
    print(p_end,length_max)
    return s1[p_end-length_max:p_end],length_max  #返回最长子串及其长度
 
s1=str(input())
s2=str(input())
res=LCS_find_Substring(s1, s2)
print(res)

2、最长公共子序列(可不连续)—python实现

       Longest Common Subsequence,LCS 一个序列S任意删除若干个字符得到新序列T,则T叫做S的子序列;两个序列X和Y的公共子序列中,长度最长的那个,定义为X和Y的最长公共子序列。
      比如:字符串"helloworld"和"loop"的最长公共子序列为loo;字符串acdfg与adfc的最长公共子序列为adf
注意:区别最长公共子串,最长公共字串要求连续,而序列可以不连续。

def LCSubsequence(string1,string2):
    len1 = len(string1)
    len2 = len(string2)
    res = [[0 for i in range(len1+1)] for j in range(len2+1)]
    for i in range(1,len2+1):
        for j in range(1,len1+1):
            if string2[i-1] == string1[j-1]:
                res[i][j] = res[i-1][j-1]+1
            else:
                res[i][j] = max(res[i-1][j],res[i][j-1])
    return res,res[-1][-1]
print(LCS("helloworld","loop"))

2、LCS的意义

(1)、求两个序列中最长的公共子序列算法,广泛的应用在图形相似处理、媒体流的相似比较、计算生物学方面。生物学家常常利用该算法进行基因序列比对,由此推测序列的结构、功能和演化过程。
(2)、LCS可以描述两段文字之间的“相似度”,即它们的雷同程度,从而能够用来辨别抄袭。另一方面,对一段文字进行修改之后,计算改动前后文字的最长公共子序列,将除此子序列外的部分提取出来,这种方法判断修改的部分,往往十分准确。简而言之,百度知道、百度百科都用得上。

3、求解

(1)、计算LCS长度



(2)、根据b提供的方向,构造最长公共子序列

(3)、最大公共子序列的多解性:求所有的LCS

4、LCS的应用—最长递增子序列LIS

T1、使用LCS解LIS问题
T2、使用动态规划来求解

5、LIS的动态规化算法

四、LCS与字符串编辑距离

1、字符串“ALGORITHM”是如何变成字符串“ALTRUISTIC”的?

参考文献
余祥宣等,计算机算法基础[M],华中科技大学出版社,2001
刘佳梅.求最长公共子序列问题的一种快速算法.中国科技论文在线[J].2010,11
李欣,舒风迪.最长公共子序列问题的改进快速算法.计算机应用研究[J].2000
郑翠玲.最长公共子序列算法的分析与实现.武夷学院学报[J],2010,29 卷(2):44~48
https://github.com/julycoding/The-Art-Of-Programming-By-July/blob/master/ebook/zh/02.04.md(最大子数组)
https://github.com/julycoding/The-Art-Of-Programming-By-July/blob/master/ebook/zh/05.02.md(字符串编辑距离)

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多