因為數據結構快學串了,以前又做過一些字符串dp的題,今天突然就想把它們寫在一起吧。
?
直接開始
問題1:給兩個字符串,求最長公共子串
問題2:給兩個字符串,求最長公共子序列
問題3:給一個字符串,求最長回文子串
問題4:給一個字符串,求最長回文子序列
問題5:給一個字符串,求將這個字符串變為回文串需要插入的最少字符個數。
問題6:最小編輯代價
問題7:判斷交錯組成
問題8:給一個字符串,求最長相同前后綴
問題9:給串a和b,判斷b是否在a中出現,若出現輸出第一次出現的位置。
問題1:給兩個字符串,求最長公共子串
串和序列的區別之前提到過,串連續,序列可以不連續
如果連續那就比較好想了,定義DP(i,j)的含義是兩個串分別以下標i和j結尾最長的公共子串長度。
如果a[i]=b[j],那DP(i,j)=DP(i-1,j-1)+1,如果DP(i-1,j-1)=0,還是同樣的操作,僅僅是之前不能構成而已,那i和j結尾的最長一定是1了。
如果a[i]!=b[j],DP(i,j)=0,因為定義是以i和j結尾,一定不存在這樣的相同子串。
初始化:先打第一行和第一列,相同為1,不同為0即可。
問題2:給兩個字符串,求最長公共子序列
舉例:
S1=“ABCBDAB.”
S2=“BABCBD.”
可以看出他們的最長公共子序列有ABCB,ABCD ,BCBD等,長度為4.
Dp(i,j)表示S的前i位與T的前j位的最長公共子串長度。
如果a[i]=b[j],那DP(i,j)=DP(i-1,j-1)+1
否則,DP(i,j)=max(DP(i-1,j),DP(i,j-1))
仔細體會,手模擬幾個就懂了。第一次想可能沒那么容易
拓展:三個字符串:純dp做
注:多個字符串要其他做法,以后數據結構學到串了或者樹了再寫。
?
問題3:給一個字符串,求最長回文子串
馬拉車算法,我確實覺得也是動態規劃思想,跳轉看詳細介紹吧
https://blog.csdn.net/hebtu666/article/details/79822584
?
問題4:給一個字符串,求最長回文子序列
對于任意字符串,如果頭尾字符相同,那么字符串的最長子序列等于去掉首尾的字符串的最長子序列加上首尾;如果首尾字符不同,則最長子序列等于去掉頭的字符串的最長子序列和去掉尾的字符串的最長子序列的較大者。
因此動態規劃的狀態轉移方程為:
設字符串為str,長度為n,p[i][j]表示第i到第j個字符間的子序列的個數(i<=j),則:
狀態初始條件:dp[i][i]=1 (i=0:n-1)
狀態轉移方程:dp[i][j]=dp[i+1][j-1] + 2? if(str[i]==str[j])
? ? ? ? ? ? ? ? ? ?dp[i][j]=max(dp[i+1][j],dp[i][j-1])? if (str[i]!=str[j])
計算dp[i][j]時需要計算dp[i+1][*]或dp[*][j-1],因此i應該從大到小,即遞減;j應該從小到大,即遞增。注意必須滿足i<=j的條件。
問題5:給一個字符串,求將這個字符串變為回文串需要插入的最少字符個數。
舉例:
ab3bd
只需變為adb3bda即可,在前面插入d,在后面插入a;
思路:
設dp(i,j)為將Ai..Aj變為回文串的最小代價,如果a[i]=a[j],那不用說了,肯定是dp(i,j)=dp(i+1,j-1),如果不相同,在前面或者后面插入一個字符,即dp(i,j)=min(dp(i,j-1),dp(i+1,j))+1
注意dp順序:
for i:=n downto 1
????for j:=i+1 to n
?
?
另一種思路:
將原串與原串的倒序做一次最長公共子序列,用原串長度減去最長公共子序列長度,即為需要插入字符的個數。邏輯很好想,不過多介紹
?
問題6:最小編輯代價
這個解釋有點麻煩,思路分的比較多,是個值得好好思考一下的題。
[題目]
?給定兩個字符串str1 和str2,再給定三個整數ic、dc 和rc,分別代表插入、刪除和替換一個字符的代價,返回將str1編輯成str2的最小代價。
(舉例]
??????str1="abc",str2="adc", ic=5, ?dc=3, ?rc=2。
??????從"abc"編輯成"adc",把b'替換成'd是代價最小的,所以返回2。str1="abc",str2="adc", ic=5, ?dc=3, ?rc=100。
??????從"abc"編輯成"adc",先刪除"b', 然后插入d是代價最小的,所以返回8。str1="abc",str2="abc", ic=5, ?dc=3, ?rc=2。
??????不用編輯了,本來就是一樣的字符串,所以返回0。
?
思路:定義dp(i,j)為str1下標i之前編輯到str2下標j之前需要的最小代價。
Dp[0][0]=0,空到空,不用改變。
矩陣dp第一列即dp[..M-1][0]。dp[i][0]表 示str1[0..-1]編輯成空串的最小代價,亳無疑問,是把str1[..i1]所有的字符刪掉的代價,所以dp[i][0]=dc*i。
.矩陣dp第一行即dp[0][..N-1]。 dp[0][j]表示空串編輯成str2[O.j-1]的最小代價,亳無疑問,是在空串里插入str2[0.j-1]所有字符的代價,所以dp[0][]=ic*j。
?
其他位置按照從左到右,再從上到下來計算,dp[i][j]的值只可能來自以下四種情況。
??????str1[0.i-1]可以先編輯成str1[..i-2], 也就是刪除字符str1[i-1], 然后由str1[0.i-2]編輯成str2[0.j-1], dp[i-1][i]表 示str1[0.i-2]編輯成 str2[0.j-1]的 最小代價,那么dp[i][j]可能等于dc+dp[i-1][j]
??????str1[0.i-1]可以先編輯成str2[0.j-2], 然后將str2[0.j-2]插入字符str2[j-1], 編輯成str2[0.j-1],dp[i][j-1]表 示str1[..i-1]編 輯成str2[0.j-2]的最小代價, 那么dp[i][j]可能等于dp[i][j-1]+ic。
??????如果str1[i-1]!=str2[j-1]。先把str1[0.i-1]中str1[..i-2]的 部分變成str2[0.j-2], 然后把字符str1[i-1]替換成str2[-1], 這樣str1[..i-1]就編輯成str2[0.j1]了 。dp[i-1][j-1]表示str1[..i-2]編輯成str2[..i-2]的最小代價,那么dp[i][j]可 能等于dp[i-1]j-1]+rc.
如果str1[i-1]==str2[j-1]. 先把str1[0..i-1]中 str1[0..i-2]的 部分變成str2[0.j-2], ?因為此時字符str1[i-1]等 于str2[j-1], 所以str1[0.i-1]已 經編輯成str2[0.j-1]了 。dp[i-1][j-1]表示str1[0i-2]編輯 成str2[..i-2]的 最小代價,那么dp[]ij]可能等于dp[i-1][j-1]
?
問題7:判斷交錯組成
給定三個字符串strl str2和aim,如果aim包含且僅包含來自str1 和str2的所有字符,而且在aim中屬于str1的字符之間保持原來在str1中的順序,屬于str2的字符之間保持原來在str2中的順序,那么稱aim是str1和str2的交錯組成。實現-一個函數,判斷aim是否是str1和str2交錯組成。
思路:做這個題一開始腦子沒開竅,老想三維,表示str1的前i個和str2的前j個,組成aim的k個,但其實k只能是i+j,所以,dp[i][j]為str1的前i個和str2的前j個能否組成aim(i+j)。
那就簡單了,要么放i要么放j,都不行就是0,有一個可以就是1.
問題8:給一個字符串,求最長相同前后綴,前綴不包括最后一個字符,后綴不包括第一個字符。
注意看kmp的next數組思想。先看下面的再看這個
https://blog.csdn.net/hebtu666/article/details/82492803
問題9:給串a和b,判斷b是否在a中出現,若出現輸出第一次出現的位置。
8和9看kmp詳解:https://blog.csdn.net/hebtu666/article/details/79822446
?
?
?
?