dp基礎之雙序列型子串出現次數

2021-09-02 14:04:13 字數 1426 閱讀 2218

問題分析:可以用類似於最長公共子串的思路。問b在a中出現的次數,考慮最後乙個字元b[n-1]和a[m-1]

case1:如果b[n-1] = a[m-1],則考慮b[0,...,n-2]在a[0,...,m-2]出現多少次

case2:如果b[n-1] != a[m-1],則考慮b[0,...,n-1]在a[0,...,m-2]出現多少次

子問題:原問題:問b[0,...,n-1]在a[0,..,m-1]中出現的次數

現在變成b[0,...,n-2]在a[0,...,m-2]出現的次數和b[0,...,n-1]在a[0,...,m-2]出現的次數

設f[i][j]表示b的前j個字元b[0,...,j-1]在a的前i個字元a[0,...,i-1]出現的次數

則f[i][j] = f[i-1][j-1](b[j-1] = a[i-1]) + f[i-1][j]

case1  +     case2

初始情況;

f[i][0] = 1,(i = 0,...,m)空串在字串裡出現的次數為1,

f[0][j] = 0,(j = 1,...,n)表示字串在空串裡出現的次數是0

計算順序:

f[0][0]...f[0][n]..

.f[m][0]...f[m][n]

答案:f[m][n]

時間複雜度o(mn),空間複雜度o(mn),同樣可以優化到o(n)

**及注釋如下:

def distinct_subs(a,b):

m ,n= len(a),len(b)

if m < n:

return 0

f = [[0 for i in range(n+1)] for j in range(m+1)]

#初始化

for i in range(m+1):

for j in range(n+1):

#f[i][0] = 1,(i = 0,...,m)

#f[0][j] = 0,(j = 1,...,n)

if j == 0:

f[i][j] = 1

continue

if i ==0 :

f[i][j] = 0

continue

#f[i][j] = f[i-1][j-1](b[j-1] = a[i-1]) + f[i-1][j]

f[i][j] = f[i-1][j]

if b[j-1] == a[i-1]:

f[i][j] += f[i-1][j-1]

return f[m][n]

a = ['r','a','b','b','b','i','t']

b = ['r','a','b','i','t']

print(distinct_subs(a,b))

#答案:3

DP之序列 子串問題

dp之序列 字串問題是非常常見的一種問題,它包括下面這些題型 最大連續子串行和 最長不下降子串行 lis 最大公共子串行 最長回文子串 這類問題的最大特點就是它的dp陣列的索引i是依據序列或字串的下標的。即根據序列seq i 或者字串s i 來定義dp i 的含義,例如lis的dp i 的定義就是以...

dp基礎之博弈型取石子

問題分析 要求面對n個石子,是否先手必勝 需要知道面對n 1和n 2個石子,是否先手必勝 子問題 設f i 表示面對i個石子,是否先手必勝 f i true false f i true f i 1 false and f i 2 false 拿一顆石子或兩顆石子都輸 true f i 1 fals...

DP入門之《LIS 最長不降子串行》

剛學dp沒思路,就去找書看,剛開始了解了記憶化搜尋與遞推,對dp問題漸漸開始有了一些理解,並有了一些自己的想法,對書上描述的狀態轉移方程開始感覺有點意思了。今天又看看了對lis的解決方案的描述,感覺貌似明白了,就試著寫了寫,只寫了20行的 就解決問題了,當然,這段 只能給出最優值,而不能給出最優方案...