1.最長公共子序列長度
對於兩個字符串,請設計一個高效算法,求他們的最長公共子序列的長度。
思路:用兩個指針i和j從後往前遍歷s1和s2,如果s1[i]==s2[j],那麼這個字符一定在lcs中;否則的話,s1[i]和s2[j]這兩個字符至少有一個不在lcs中,需要丟棄一個。
步驟:1.明確dp數組含義 dp[i][j]的含義是:對於s1[1…i]和s2[1…j],它們的 LCS 長度是dp[i][j]。
2.定義base case 讓索引爲 0 的行和列表示空串,dp[0][…]和dp[…][0]都應該初始化爲 0,這就是 base case。
3.找狀態轉移方程:
str1=input("")
str2=input("")
n=len(str1)
m=len(str2)
dp=[None]*(n+1)
for i in range(n+1):
dp[i]=[0]*(m+1)
dp[0][0]=0
for i in range(1,n+1):
for j in range(1,m+1):
if str1[i-1]==str2[j-1]:
dp[i][j]=dp[i-1][j-1]+1
else:
dp[i][j]=max(dp[i-1][j], dp[i][j-1])
print(dp[n][m])
2.最長公共子序列序列
思路:從dp矩陣中最後位置開始遍歷:如果str[i]==str[j]則記錄下當前位置的字符,並前i-1,j-1向前走。如果不相等,則比較dp[i-1][j]與dp[i][j-1] 選擇序列較大的位置進行向前遍歷。
if dp[n][m]==0:
print(-1)
else:
lis=[None]*(dp[n][m])
i=n
j=m
k=dp[n][m]-1
while i>=1 and j>=1:
if str1[i-1]==str2[j-1]:
lis[k]=str1[i-1]
k-=1
i-=1
j-=1
else:
if dp[i][j-1]>=dp[i-1][j]:
j=j-1
elif dp[i][j-1]<dp[i-1][j]:
i=i-1
s1=''.join(lis)
print(s1)
3.將整數N分爲K份,總共有多少種分法? (類似:n個蘋果放入m個盤子有多少種方法)
思路 :因爲每份至少要分1,所以先取K份每份分1,剩下數字(N-K)任意分,可以是分在一份,兩份,…到K份
所以有 D[N,K]=D[D-K][1]+D[D-K][2]+***+D[D-K][K] ①
D[N-1][K-1]=D[(D-1)-(K-1)][1]+***+D[(D-1)-(K-1)][K-1] ②
由①②兩式有:D[N,K]=D[N-1][K-1]+D[D-K][K] (動態轉移方程)
編程如下:
N,K=[int(i) for i in input().split()]
dp=[None]*100
for i in range(100):
dp[i]=[0]*10
dp[0][0]=1
print(dp)
for i in range(1, N+1):
for j in range(1, K+1):
if i>=j:
dp[i][j]=dp[i-1][j-1]+dp[i-j][j]
print(dp[N][K])
4.給定一個正整數 n,將其拆分爲至少兩個正整數的和,並使這些整數的乘積最大化。 返回你可以獲得的最大乘積。等價於剪繩子:(劍指offer14題)
//dp[i]表示:數字 i 拆分爲至少兩個正整數之和的最大乘積
n=int(input(""))
dp = [None]*(n+1)
for i in range(n+1):
dp[i]=0
#dp[i]表示整數i被拆分可以得到的最大乘積
dp[1] = 1
dp[2] = 1
#i可以被分爲j與i-j
for i in range(3, n+1):
#j的範圍是1 - i-1
for j in range(1, i):
#(i-j)*j不一定是最大乘積,因爲i-j不一定大於dp[i - j]
dp[i]=max(dp[i], max(dp[i-j]*j, (i-j)*j))
print(dp[n])