最長公共子序列 & 最長公共子串的區別:python
找兩個字符串的最長公共子串,這個子串要求在原字符串中是連續的。而最長公共子序列則並不要求連續。數組
題目: 找出兩個字符串的最長連續公共子串
例: 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)
結果以下:索引
題目: 找出兩個字符串的最長公共子序列(非連續)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)
結果以下:
題目:給定一個字符串 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
題目:一個一維數組中只有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)
題目:給定一個含有 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
題目:給定一個整數數組 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