在牛客網上刷面經的過程當中,發現面試常問的兩道題,這裏記錄下來,方便之後複習查看。ios
#include<iostream> #include<cstring> #include<cstdio> #define N 1005 using namespace std; int dp[N][N]; /* dp[i][j]表示 P1[1...i]與 P2[1...j]的最長公共子序列LCS 初始狀態:某一個排列爲空,那麼LCS確定爲0,故很容易推斷出 dp[0][1..n]=0, dp[1..n][0] 一、if P1[n]==P2[n],則 dp[n][n]=dp[n-1][n-1] + 1 二、if P1[n]!=P2[n], 則 dp[n][n] = max(dp[n][n-1], dp[n-1][n]) Tips: 爲了提升效率,採用自底向上計算的方法。 也即先計算dp[1][1]... 最後計算dp[n][n],由於dp[n][n]確定要用到以前的狀態嘛 */ int solve(string p1, string p2, int p1_len, int p2_len){ for(int i=1;i<=p1_len;i++){ for(int j=1;j<=p2_len;j++){ if(p1[i-1]==p2[j-1]){ dp[i][j] = dp[i-1][j-1] + 1; }else{ dp[i][j] = max(dp[i-1][j], dp[i][j-1]); } } } return dp[p1_len][p2_len]; } int main(){ string p1, p2; while(cin>>p1>>p2){ memset(dp, 0, sizeof(dp)); int p1_len = p1.length(); int p2_len = p2.length(); cout<<solve(p1, p2, p1_len, p2_len)<<endl; } return 0; }
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #define N 1005 using namespace std; int p1[N]; int n; int dp[N]; /* dp[i]表示 P[0...i] 的最長上升子序列 LIS 代碼中solve_1 方法一 : 直接dp,O(n^2) 一、dp[0] = 1 二、dp[i] = max(dp[i], dp[j]) + 1, 0<=j<i, P[i]>P[j](由於只有大於纔是上升的序列) 方法二 :貪心 + 二分,O(nlogn) 代碼中solve_2 貪心:對於固定某一長度LIS而言,最後一個數字越小,越有可能添加新元素 如長度爲2的LIS{1,2}比{1,3}好! 一、咱們維護一個dp數組, dp[i]表示P[0..i]這個長度爲i+1的序列 LIS最小值 二、利用二分查找找到位置,並插入之 三、最後結果即爲dp數組的長度 */ int solve_1(int p1[], int p1_len){ int ans = 0; for(int i=0;i<p1_len;i++){ dp[i]=1; } for(int i=0;i<p1_len;i++){ for(int j=0;j<i;j++){ if(p1[i]>p1[j]) dp[i] = max(dp[i], dp[j]+1); } ans = max(ans, dp[i]); } return ans; } int solve_2(int p1[], int p1_len){ int pos = 0; dp[0]=p1[0]; for(int i=1;i<p1_len;i++){ if(p1[i]>dp[pos]){ dp[++pos] = p1[i]; }else{ int *idx = lower_bound(dp, dp+pos+1, p1[i]); //cout<<*idx<<endl; dp[idx-dp] = p1[i]; } } return pos+1; } int main(){ cin>>n; for(int i=0;i<n;i++){ int m;cin>>m; p1[i]=m; } //cout<<solve_1(p1, n)<<endl; cout<<solve_2(p1, n)<<endl; return 0; }