腾讯--构造回文
騰訊–構造回文
文章目錄
- 騰訊--構造回文
- 一、題目描述
- 二、分析
- 三、代碼
一、題目描述
給定一個字符串s,你可以從中刪除一些字符,使得剩下的串是一個回文串。如何刪除才能使得回文串最長呢?
輸出需要刪除的字符個數。
- 輸入描述:
- 輸出描述:
二、分析
這道題其實和:最?公共?序列:最?公共?序列問題解法相似。
- 為了和最?公共?序列這道題一致,我們先用已知字符串構造一個新的字符串并翻轉一下(翻轉目的是:如果對應位置相等,就說明滿足回文關系),那么s1和s2就構成兩個字符串,而題目中要求我們刪除字符串使剩下的字符串構成最長的回文串,那么我們是否可以理解為求兩個字符串中最長的公共子序列呢?
- 首先題目求的是回文序列,那么s2是s1翻轉過來的,那么如果滿足回文關系,那么對應相同位置上的字符肯定是相等的,是不是對應最長公共子序列當中的公共
- 同理,題目中要求是刪除字符使s1和s2構成最長的回文串,那么是否就對應著最長公共子序列當中的序列(串:字符之間必須連續;序列:可以不連續)。
- 所以這道題可以借鑒最長回味子序列的解法,這里我們在寫一遍
- 這道題可以用dp數組求解;同樣是套路;動態規劃的第一步:明確狀態和選擇,對于這道題來說,狀態就是回文串的長度,選擇就是是否刪除某個字符
- 所以對于dp數組來說含義就明確了:dp[i][j]:對于 s1[1…i] 和 s2[1…j] ,它們的 最長回文字串的?度是 dp[i][j]
- 第?步, 定義 base case
- 我們專門讓索引為 0 的?和列表?空串, dp[0][…] 和 dp[…][0] 都應該初始化為 0, 這就是 base case。
- ?如說, 按照剛才 dp 數組的定義, dp[0][3]=0 的含義是: 對于字符串"" 和 “bab” , 其最長回文字串的?度是 0。 因為有?個字符串是空串, 它們的最長回文字串的?度是顯然應該是 0
- 第三步, 找狀態轉移?程。
- 狀態轉移說簡單些就是做選擇, ?如說這個問題, 是求 s1 和 s2 的最長回文字串的?度是, 不妨稱這個?序列為 lcs 。 那么對于 s1 和 s2 中的每個字符, 有什么選擇?
- 很簡單, 兩種選擇, 要么在 lcs 中, 要么不在
- 這個「在」 和「不在」 就是選擇, 關鍵是, 應該如何選擇呢?
- 這個需要動點腦筋: 如果某個字符應該在 lcs 中, 那么這個字符肯定同時存在于 s1 和s2 中, 因為 lcs 是最長回文字串嘛。
- 所以本題的思路是這樣:
- ?兩個指針 i 和 j 從后往前遍歷 s1 和 s2 , 如果 s1[i]==s2[j] , 那么這個字符?定在 lcs 中; 否則的話, s1[i] 和 s2[j] 這兩個字符?少有?個不在 lcs 中, 需要丟棄?個。
- 分析完就可以整代碼了
三、代碼
#include <iostream> #include <string.h> #include <algorithm> using namespace std;const int MAXN=1010; int dp[MAXN][MAXN];//先求s的反串reverse,然后求他們的最長的公共子序列,要刪除的字符個數就能知道 //時間復雜度O(N^2)int getRemoveNumber(string &s1) {string s2(s1);reverse(s2.begin(),s2.end());int len=s1.length();memset(dp,0,sizeof dp);//講二維數組全部替換成0for(int i=0;i<len;++i){for(int j=0;j<len;++j){if(s1[i]==s2[j])//判斷如果遍歷到相等的字符,dp[i+1][j+1]=dp[i][j]+1;else dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]);}}return len-dp[len][len]; }int main() {string s;while(cin>>s){cout<<getRemoveNumber(s)<<endl;}return 0; }當然可以用備忘錄優化一下!!!
總結
- 上一篇: 二叉树构建及双向链表
- 下一篇: 美团/力扣(647)--回文字串