1. 程式人生 > >SPOJ33&POJ1934 Trip DP、枚舉

SPOJ33&POJ1934 Trip DP、枚舉

eof end style www. 一個 problem rip for void

題目傳送門:https://www.luogu.org/problemnew/show/SP33

題目大意:給出兩個字符串,求其LCS(最長公共子序列)的長度與具體方案(相同的串算作同一方案)。數據組數$\leq 10$,字符串長度$\leq 80$,方案數$\leq 1000$


本來以為這是一道LCS水題,結果超級low的各種輸出方案的方法TLE到懷疑人生

於是一個高大上的輸出方法出現(借鑒於https://blog.csdn.net/gg_gogoing/article/details/41170117)

多開兩個輔助數組$f_{i,j}$與$g_{i,j}$代表$s1$與$s2$從第$1$個字符到第$i$個字符中字符$j$($a$對應$0$,$b$對應$1$,以此類推)最後一次出現的位置,計算完這兩個數組之後,進行遞歸輸出,同時使用枚舉法,從後往前一個一個枚舉當前位置可填的字母。


設$dfs( a , b , c)$表示當前$s1$的長度為$a$,$s2$的長度為$b$,待枚舉的字母數量為$c$時的枚舉過程,枚舉$0-25$,得到該字母的$f_{a,b}$與$g_{a,b}$,如果當$s1$長度為$f_{a,b}$,$s2$長度為$g_{a,b}$時的最長公共子序列長度正好為$c$,則$dfs(f_{a,b} - 1 , g_{a,b} - 1 , c - 1)$,枚舉完第一位後即得到一種滿足題意的字符串

為何這樣子可以大量剪枝?舉例:

$abcabcaa$
$acbacba$

上面是樣例數據,最長公共子序列長度為$5$。考慮$dfs(7,6,5)$時枚舉到$a$(字符串從$0$開始標號),此時$f = 7 , g = 6$,會進行$dfs(6,5,4)$。如果不考慮上述枚舉,我們的可選方案有$(0,0)(0,3)(0,6)(3,0)(3,3)(3,6)(6,0)(6,3)(6,6)(7,0)(7,3)(7,6)$共$12$種,因為相同的串算作同一方案,由貪心可以知道$(7,6)$是當前的最優選擇,可以包含其他的所有情況,所以只需要向$(7,6)$繼續搜索即可。

#include<bits/stdc++.h>
using namespace std;
string s1 , s2;
vector < string > s;
short maxN[81][81] , last1[26][81] , last2[26][81] , cou;
//last1、last2對應上面的f、g。
inline int max(int a , int b){
    return a > b ? a : b;
}
void create(int a1 , int a2 , int num , string ss){
    if(num == 0
){ s.push_back(ss); return; } for(int i = 0 ; i < 26 ; i++) if(last1[i][a1] >= num && last2[i][a2] >= num && maxN[last1[i][a1]][last2[i][a2]] == num) create(last1[i][a1] - 1 , last2[i][a2] - 1 , num - 1 , (char)(a + i) + ss); //遞歸輸出最重要的過程!!! } int main(){ ios::sync_with_stdio(0); int T; for(cin >> T ; T ; T--){ memset(maxN , 0 , sizeof(maxN)); memset(last1 , 0 , sizeof(last1)); memset(last2 , 0 , sizeof(last2)); cin >> s1 >> s2; for(int i = 1 ; i <= s1.size() ; i++) for(int j = 1 ; j <= s2.size() ; j++){ if(s1[i - 1] == s2[j - 1]) maxN[i][j] = max(maxN[i][j] , maxN[i - 1][j - 1] + 1); maxN[i][j] = max(maxN[i][j] , max(maxN[i - 1][j] , maxN[i][j - 1])); } //LCS DP求解 for(int i = 1 ; i <= s1.size() ; i++) for(int j = 0 ; j < 26 ; j++) if(s1[i - 1] - a == j) last1[j][i] = i; else last1[j][i] = last1[j][i - 1]; for(int i = 1 ; i <= s2.size() ; i++) for(int j = 0 ; j < 26 ; j++) if(s2[i - 1] - a == j) last2[j][i] = i; else last2[j][i] = last2[j][i - 1]; create(s1.size() , s2.size() , maxN[s1.size()][s2.size()] , ""); sort(s.begin() , s.end()); for(int i = 0 ; i < s.size() ; i++) cout << s[i] << endl; //輸出 s.clear(); cout << endl; cou = 0; } return 0; }

SPOJ33&POJ1934 Trip DP、枚舉