8.2 數據結構---字符串(查找)

最長公共子序列 & 最長公共子串的區別:python

找兩個字符串的最長公共子串,這個子串要求在原字符串中是連續的。而最長公共子序列則並不要求連續。數組

1、最長連續公共子串

題目:  找出兩個字符串的最長連續公共子串
例: abccade 和 dgcadde ==> cad

思路:動態規劃 app

考慮兩種狀況:ide

M[i+1][j+1]=0,             s1[i+1] != s2[j+1]3d

M[i+1][j+1]=M[i][j]+1,  s1[i+1] == s2[j+1]code


時間複雜度O(M*N)
空間複雜度O(M*N)blog

 

代碼以下:遞歸

def getMaxSubStr(s1,s2):
    len_s1 = len(s1)
    len_s2 = len(s2)
    sb = ''
    maxs = 0 #記錄最長公共子串的長度
    maxI = 0 #記錄最長公共子串的最後一個字符的位置
    M = [([None] * (len_s2+1)) for i in range(len_s1+1)]
    i = 0
    while i < len_s1 + 1:
        M[i][0] = 0
        i += 1
    j = 0
    while j < len_s2 + 1:
        M[0][j] = 0
        j += 1
    #經過利用遞歸公式填寫新建的二維數組
    i = 1
    while i < len_s1 + 1:
        j = 1
        while j < len_s2 + 1:
            if list(s1)[i-1] == list(s2)[j-1]:
                M[i][j] = M[i-1][j-1] + 1
                if M[i][j] > maxs:
                    maxs = M[i][j]
                    maxI = i
            else:
                M[i][j] = 0
            j += 1
        i += 1
    i = maxI - maxs
    while i < maxI:
        sb = sb + list(s1)[i]
        i += 1
    return sb

s1 = 'abcdefg'
s2 = 'bdeg'
res = getMaxSubStr(s1,s2)
print(res)

結果以下:索引

 

2、最長公共子序列(非必須連續)

題目: 找出兩個字符串的最長公共子序列(非連續)leetcode

舉例: abcbdab和bdcaba ==》 bcba

思路:動態規劃,

    M[i][j]=0,            i=0,j=0

    M[i][j]=M[i-1][j-1] + 1             i,j>0,xi=yi

    M[i][j]=max{M[i-1][j],M[i][j-1]}   i,j>0,xi!=yi

 

        S[i][j]=1        s1[i] == s2[j]

     S[i][j]=2        s1[i] != s2[j] 且 M[i-1][j] >=M[i][j-1]

     S[i][j]=3        s1[i] != s2[j] 且 M[i-1][j] < M[i][j-1]

 

代碼以下:

def LCS(s1,s2):
    #s1行,s2列
    len_s1 = len(s1)
    len_s2 = len(s2)
    sb = ''
    M = [([None] * (len_s2+1)) for i in range(len_s1+1)]
    S = [([None] * (len_s2+1)) for i in range(len_s1+1)]
    i = 0
    while i < len_s1 + 1:
        M[i][0] = 0
        S[i][0] = 0
        i += 1
    j = 0
    while j < len_s2 + 1:
        M[0][j] = 0
        S[0][j] = 0
        j += 1
    #經過利用遞歸公式填寫新建的二維數組
    i = 1
    while i < len_s1 + 1:
        j = 1
        while j < len_s2 + 1:
            if s1[i-1] == s2[j-1]:
                M[i][j] = M[i-1][j-1] + 1
                S[i][j] = 1
            elif M[i-1][j] >= M[i][j-1]:
                M[i][j] = M[i-1][j]
                S[i][j] = 2
            else:
                M[i][j] = M[i][j-1]
                S[i][j] = 3
            j += 1
        i += 1
    # print(M)
    return M[-1][-1],S

def cLCS(i,j,S,s1):
    if i == 0 or j == 0:
        return
    if S[i][j] == 1:
        cLCS(i-1,j-1,S,s1)
        print (s1[i - 1], end='')
    elif S[i][j] == 2:
        cLCS(i-1,j,S,s1)
    else:
        cLCS(i,j-1,S,s1)

s1 = 'abcbdab'
s2 = 'bdcaba'
max,S = LCS(s1,s2)
print(S)
# print(len(S),len(S[0]))
cLCS(len(S)-1,len(S[0])-1,S,s1)
# print(max)

 

結果以下:

 

3、求字符串裏的最長迴文子串

題目:給定一個字符串 s,找到 s 中最長的迴文子串。你能夠假設 s 的最大長度爲 1000。

舉例:'cdca'的最長迴文字符串爲'cdc'

思路:遍歷字符串的每一個元素,而後以該元素爲中心點進行左右擴展,取長度最大的

 

代碼以下:

class Solution():
    def __init__(self):
        self.max_len = 0
        self.res = ''

    def getLongestPalindrome(self,s):
        if len(s) == 1:
            return
        start = 0
        for i in range(1,len(s)):
            tmp1 = self.max_side(s,i,i) #以這個數爲中心點進行擴展
            if tmp1 > self.max_len:
                self.max_len = tmp1
                start = i - tmp1 // 2

            tmp2 = self.max_side(s,i-1,i)#從這個數和前面的數=以兩個數爲中心點進行擴展
            if tmp2 > self.max_len:
                self.max_len = tmp2
                start = i - tmp2 // 2
        self.res = s[start:start+self.max_len]
        return s[start:start+self.max_len]

    def max_side(self,s,i,j):
        maxs = 0
        if i == j: #單數是以一個數爲中心
            maxs  = 1
            i -= 1
            j += 1

        while i >= 0 and j < len(s) and s[i] == s[j]: #雙數以兩個同樣的字符爲中心
            maxs += 2
            i -= 1
            j += 1
        return maxs

    #leetcode速度最快的代碼
    def longestPalindrome_best(self, s):
        """
        :type s: str
        :rtype: str
        """
        length = len(s)
        if length < 2 or s == s[::-1]: return s
        max_len, begin = 1, 0
        for i in range(1, length):
            odd = s[i - max_len - 1:i + 1]
            even = s[i - max_len:i + 1]
            if i - max_len >= 1 and odd == odd[::-1]:
                begin = i - max_len - 1
                max_len += 2
                continue
            if i - max_len >= 0 and even == even[::-1]:
                begin = i - max_len
                max_len += 1
        return s[begin:begin + max_len]

S = Solution()
res = S.longestPalindrome_best(s='abaad')
print(res)

結果以下:aba

 

4、和爲0的最長連續子串長度

題目:一個一維數組中只有1和-1,實現程序,求和爲0的最長子串長度,並在註釋中給出時間和空間複雜度

思路:在i從0到n,計算sum(i),sum(i)表示從0到i的元素之和。並保存在字典dic中,value是索引i,在日後的遍歷中每獲得一個sum(i)就查看dic的keys是否已有此sum(i)值,若是有則用當前i位置減去保存的i,並與maxLen比較,取大的那個。遍歷結束,給出結果。時間複雜度O(n),空間複雜度O(1)

代碼以下:

def min_len(l):
    dic = {0: -1}
    sum = 0
    maxLen = 0
    for x in range(0, len (l)):
        sum += l[x]
        print(dic)
        if sum in dic:#若是有同樣的數出現,說明兩個數之間的數和第二個數之和等於0
            maxLen = max(maxLen, x - dic[sum])
        else:
            dic[sum] = x
    return maxLen

print(min_len([3,5,-1,-6,2]))

 

【擴展】和爲給定值的最長連續子串

思路:遍歷,找和爲s的子串,留長度最大的

代碼以下:

def findarr(s,nums):
    if not nums:
        return 0
    res = -2 ** 31
    for i in range(4,len(nums)):
        pos = i + 1
        while pos < len(nums)-2 and sum(nums[i:pos+1]) < s:
            pos += 1
        if sum(nums[i:pos+2]) == s and pos - i + 1 > res:
            print(i,pos)
            res = pos - i + 1
    print(res)

s = 7
nums = [2,3,0,2,4,2,0,0,1,2,0,0,2,2]
findarr(s,nums)

 

  

 

5、和大於等於給定值的最短連續子串

題目:給定一個含有 n 個正整數的數組和一個正整數 s ,找出該數組中知足其和 ≥ s 的長度最小的連續子數組。若是不存在符合條件的連續子數組,返回 0。

舉例:輸入: s = 7, nums = [2,3,1,2,4,3]      輸出: 2

解釋: 子數組 [4,3] 是該條件下的長度最小的連續子數組。

思路1:遍歷每位,找和大於等於給定值的長度,而後依次向後遍歷,直到遍歷完全部的位置。

思路2:滑動窗口,從左往右加到大於s的數,而後從左開始刪,若刪除以後還能獲得大於s的數,則記錄當前的長度,若不能,就繼續右移,加數

 

思路1代碼以下:

def findarr(s,nums):
    # nums.sort() #[4,3,3,2,2,1]
    if not nums:
        return 0
    res = 2 ** 31
    for i in range(len(nums)):
        pos = i + 1
        while pos < len(nums)-2 and sum(nums[i:pos+1]) < s:
            pos += 1
        if pos - i + 1 < res:
            print(i,pos)
            res = pos - i + 1
    print(res)

  

 思路2代碼以下:

def minSubArrayLen2(s, nums):
    cur_sum = 0
    n = len (nums)
    res = float ("inf")
    l = 0
    for i in range (n):
        cur_sum += nums[i]
        while cur_sum >= s:
            res = min (res, i - l + 1)
            cur_sum -= nums[l]
            l += 1
    return res if res != float ("inf") else 0

s = 7
nums = [2,3,1,2,4,3]
res = minSubArrayLen2(s,nums)
print(res)

結果:res = 2  

 

6、連續最大子序和

 題目:給定一個整數數組 nums ,找到一個具備最大和的連續子數組(子數組最少包含一個元素),返回其最大和。

示例: 輸入: [-2,1,-3,4,-1,2,1,-5,4],  輸出: 6

解釋: 連續子數組 [4,-1,2,1] 的和最大,爲 6。

進階: 若是你已經實現複雜度爲 O(n) 的解法,嘗試使用更爲精妙的分治法求解。 O(n)

 

思路1:始終保留最大值,若是當前和比n還小,當前和就取n;不然,和加上這個數,而後用c_res記錄最大子序列

代碼以下:

def maxSubArray1(nums):
    s, ts = - 2 ** 31, - 2 ** 31
    res_ = []
    c_res = []
    for n in nums:
        if n > ts + n:  #若是當前和比n還小,當前最大和就取n
            ts = n
            res_ = [n]
        else:   #不然,取n+ts
            ts = n + ts
            res_.append(n)
        if s < ts:
            s = ts
            c_res = list(tuple(res_))
            print("c_res=%s,res_=%s"%(c_res,res_))#c_res記錄最大子序列
    return s

# res = maxSubArray1([1,-2])
# print(res)

  

思路2:若是把數組分紅左右兩段,那麼加和最大的連續子序列,要麼出如今數組的左半部分,要麼出如今數組的右半部分,要麼出如今中間,即從左半部分和右半部分相鄰的地方各區一段。因此能夠用分治法來求解,具體實現時須要藉助遞歸

代碼以下:

import math
def CalMax(a, b, c):#三個數比較大小
    if a > b:
        if a > c:
            return a
        else:
            return c
    else:
        if b > c:
            return b
        else:
            return c


MaxLeftSum = 0
MaxRightSum = 0
number = [7, 0, 6, -1, 1, -6, 7, -5]


def MaxCalculator(left, right):
    middle = int(math.modf((left + right) / 2)[1])
    if left == right:
        if number[left] > 0:
            return number[left]
        else:
            return 0

    MaxLeftSum = MaxCalculator(left, middle)
    MaxRightSum = MaxCalculator(middle + 1, right)
    MLASum = 0
    MRASum = 0
    MSum = 0

    i = middle
    while i >= left:
        MSum += number[i]
        if MSum > MLASum:
            MLASum = MSum
        i = i - 1
    MSum = 0

    i = middle + 1
    while i <= right:
        MSum += number[i]
        if MSum > MRASum:
            MRASum = MSum
        i = i + 1
    return CalMax(MaxLeftSum, MaxRightSum, MLASum + MRASum)

n=6
result = MaxCalculator(0,n-1)
print(result)

結果:13

相關文章
相關標籤/搜索