js把base64串解析成中文_回文问题终极篇:最小代价构造回文串
學算法認準?labuladong
東哥帶你手把手撕力扣?
點擊下方卡片即可搜索?
讀完本文,你可以去力扣完成第?1312 題「讓字符串成為回文串的最少插入次數」,難度 Hard。
回文串就是正著讀反著讀都一樣的字符串,面試筆試中經常出現回文相關的題目,我們之前有好幾篇講解回文問題的文章,是判斷回文串或者尋找最長回文串/子序列的:
經典面試題:最長回文子串
子序列解題模板:最長回文子序列
如何高效判斷回文單鏈表?
本文就來研究一道「構造回文串的最小插入次數」的問題,然后所有回文相關的問題你都可以搞定了,如果再遇到回文算法題,就偷著樂吧~
這次的題目比較困難,讓字符串成為回文串的最少插入次數:
函數簽名如下:
int?minInsertions(string?s);
比如說輸入s = "abcea",算法返回 2,因為可以給s插入 2 個字符變成回文串"abeceba"或者"aebcbea"。如果輸入s = "aba",則算法返回 0,因為s已經是回文串,不用插入任何字符。
思路解析
首先,要找最少的插入次數,那肯定得窮舉嘍,如果我們用暴力算法窮舉出所有插入方法,時間復雜度是多少?
每次都可以在兩個字符的中間插入任意一個字符,外加判斷字符串是否為回文字符串,這時間復雜度肯定爆炸,是指數級。
那么無疑,這個問題需要使用動態規劃技巧來解決。之前的文章說過,回文問題一般都是從字符串的中間向兩端擴散,構造回文串也是類似的。
我們定義一個二維的dp數組,dp[i][j]的定義如下:對字符串s[i..j],最少需要進行dp[i][j]次插入才能變成回文串。
我們想求整個s的最少插入次數,根據這個定義,也就是想求dp[0][n-1]的大小(n為s的長度)。
同時,base case 也很容易想到,當i == j時dp[i][j] = 0,因為當i == j時s[i..j]就是一個字符,本身就是回文串,所以不需要進行任何插入操作。
接下來就是動態規劃的重頭戲了,利用數學歸納法思考狀態轉移方程。
狀態轉移方程
狀態轉移就是從小規模問題的答案推導更大規模問題的答案,從 base case 向其他狀態推導嘛。如果我們現在想計算dp[i][j]的值,而且假設我們已經計算出了子問題dp[i+1][j-1]的值了,你能不能想辦法推出dp[i][j]的值呢?
既然已經算出dp[i+1][j-1],即知道了s[i+1..j-1]成為回文串的最小插入次數,那么也就可以認為s[i+1..j-1]已經是一個回文串了,所以通過dp[i+1][j-1]推導dp[i][j]的關鍵就在于s[i]和s[j]這兩個字符。
這個得分情況討論,如果s[i] == s[j]的話,我們不需要進行任何插入,只要知道如何把s[i+1..j-1]變成回文串即可:
翻譯成代碼就是這樣:
if?(s[i]?==?s[j])?{
????dp[i][j]?=?dp[i?+?1][j?-?1];
}
如果s[i] != s[j]的話,就比較麻煩了,比如下面這種情況:
最簡單的想法就是,先把s[j]插到s[i]右邊,同時把s[i]插到s[j]右邊,這樣構造出來的字符串一定是回文串:
PS:當然,把s[j]插到s[i]左邊,然后把s[i]插到s[j]左邊也是一樣的,后面會分析。
但是,這是不是就意味著代碼可以直接這樣寫呢?
if?(s[i]?!=?s[j])?{
????//?把?s[j]?插到?s[i]?右邊,把?s[i]?插到?s[j]?右邊
????dp[i][j]?=?dp[i?+?1][j?-?1]?+?2;
}
不對,比如說如下這兩種情況,只需要插入一個字符即可使得s[i..j]變成回文:
所以說,當s[i] != s[j]時,無腦插入兩次肯定是可以讓s[i..j]變成回文串,但是不一定是插入次數最少的,最優的插入方案應該被拆解成如下流程:
步驟一,做選擇,先將s[i..j-1]或者s[i+1..j]變成回文串。怎么做選擇呢?誰變成回文串的插入次數少,就選誰唄。
比如圖二的情況,將s[i+1..j]變成回文串的代價小,因為它本身就是回文串,根本不需要插入;同理,對于圖三,將s[i..j-1]變成回文串的代價更小。
然而,如果 s[i+1..j]和s[i..j-1]都不是回文串,都至少需要插入一個字符才能變成回文,所以選擇哪一個都一樣:
那我怎么知道s[i+1..j]和s[i..j-1]誰變成回文串的代價更小呢?
回頭看看dp數組的定義是什么,dp[i+1][j]和dp[i][j-1]不就是它們變成回文串的代價么?
步驟二,根據步驟一的選擇,將s[i..j]變成回文。
如果你在步驟一中選擇把s[i+1..j]變成回文串,那么在s[i+1..j]右邊插入一個字符s[i]一定可以將s[i..j]變成回文;同理,如果在步驟一中選擇把s[i..j-1]變成回文串,在s[i..j-1]左邊插入一個字符s[j]一定可以將s[i..j]變成回文。
那么根據剛才對dp數組的定義以及以上的分析,s[i] != s[j]時的代碼邏輯如下:
if?(s[i]?!=?s[j])?{
????//?步驟一選擇代價較小的
????//?步驟二必然要進行一次插入
????dp[i][j]?=?min(dp[i?+?1][j],?dp[i][j?-?1])?+?1;
}
綜合起來,狀態轉移方程如下:
if?(s[i]?==?s[j])?{
????dp[i][j]?=?dp[i?+?1][j?-?1];
}?else?{
????dp[i][j]?=?min(dp[i?+?1][j],?dp[i][j?-?1])?+?1;
}
這就是動態規劃算法核心,我們可以直接寫出解法代碼了。
代碼實現
首先想想 base case 是什么,當i == j時dp[i][j] = 0,因為這時候s[i..j]就是單個字符,本身就是回文串,不需要任何插入;最終的答案是dp[0][n-1](n是字符串s的長度)。那么 dp table 長這樣:
又因為狀態轉移方程中dp[i][j]和dp[i+1][j],dp[i]-1],dp[i+1][j-1]三個狀態有關,為了保證每次計算dp[i][j]時,這三個狀態都已經被計算,我們一般選擇從下向上,從左到右遍歷dp數組:
完整代碼如下:
int?minInsertions(string?s)?{
????int?n?=?s.size();
????//?定義:對 s[i..j],最少需要插入 dp[i][j]?次才能變成回文
????vector<vector<int>>?dp(n,?vector<int>(n,?0));
????// base case:i == j 時 dp[i][j]?=?0,單個字符本身就是回文
????//?dp?數組已經全部初始化為?0,base?case?已初始化
????//?從下向上遍歷
????for?(int?i?=?n?-?2;?i?>=?0;?i--)?{
????????//?從左向右遍歷
????????for?(int?j?=?i?+?1;?j?????????????//?根據?s[i]?和?s[j]?進行狀態轉移
????????????if?(s[i]?==?s[j])?{
????????????????dp[i][j]?=?dp[i?+?1][j?-?1];
????????????}?else?{
????????????????dp[i][j]?=?min(dp[i?+?1][j],?dp[i][j?-?1])?+?1;
????????????}
????????}
????}
????//?根據?dp?數組的定義,題目要求的答案
????return?dp[0][n?-?1];
}
現在這道題就解決了,時間和空間復雜度都是 O(N^2)。還有一個小優化,注意到dp數組的狀態之和它相鄰的狀態有關,所以dp數組是可以壓縮成一維的:
int?minInsertions(string?s)?{
????int?n?=?s.size();
????vector<int>?dp(n,?0);
????int?temp?=?0;
????for?(int?i?=?n?-?2;?i?>=?0;?i--)?{
????????//?記錄?dp[i+1][j-1]
????????int?pre?=?0;
????????for?(int?j?=?i?+?1;?j?????????????temp?=?dp[j];
????????????if?(s[i]?==?s[j])?{
????????????????//?dp[i][j]?=?dp[i+1][j-1];
????????????????dp[j]?=?pre;
????????????}?else?{
????????????????//?dp[i][j]?=?min(dp[i+1][j],?dp[i][j-1])?+?1;
????????????????dp[j]?=?=min(dp[j],?dp[j?-?1])?+?1;
????????????}
????????????pre?=?temp;
????????}
????}
????return?dp[n?-?1];
}
至于這個狀態壓縮是怎么做的,我們前文 狀態壓縮技巧:動態規劃的降維打擊?詳細介紹過,這里就不展開了。
往期推薦??
二叉樹的題,就那幾個框架,枯燥至極?
狀態壓縮技巧:動態規劃的降維打擊
一個函數秒殺 2Sum 3Sum 4Sum 問題
回溯算法和動態規劃,到底誰是誰爹
東哥手寫正則通配符算法,結構清晰,包教包會!
_____________
學好算法全靠套路,認準 labuladong 就夠了。
知乎、B站搜索:labuladong
《labuladong的算法小抄》即將出版,公眾號后臺回復關鍵詞「pdf」下載,回復「進群」可加入刷題群。
總結
以上是生活随笔為你收集整理的js把base64串解析成中文_回文问题终极篇:最小代价构造回文串的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 侮辱我的美是哪首歌啊?
- 下一篇: token验证失败_ASP.NET CO