1. 程式人生 > >[HDU4899] [2014多校聯考4] Hero meet devil [LCS][狀態壓縮][dp巢狀]

[HDU4899] [2014多校聯考4] Hero meet devil [LCS][狀態壓縮][dp巢狀]

題意:給一DNAS。 對每個i≤|S|,求有多少長度為M、與串S的最長公共子序列長度為iDNAT。 結果mod 1e9+7|S|≤15, M≤1000

15(警覺) 自然的想法是考慮列舉LCS 然後狀壓dpdp的時候判斷是不是真的是lcs。 然而這樣4n

如果儲存LCS長度或者結束位置的話,可能出現衝突(同一個狀態有不同且衝突的轉移)。 消除後效性需要將當前處理到的T的最後一位與S每一位的匹配狀態都記錄下來。 然而考慮到n2 lcs可以線上求,所以可以把lcs和狀壓合一起。

回顧一下n2LCS求法。 F(i,j) = max{ F(i,j-1), F(i-1,j), F(i-1,j-1)+(Si

==Tj) } j可以線上。所以就可以巢狀進狀壓dp裡面了。 具體方式就是在狀壓裡面記錄當前在LCS裡面匹配到哪裡, 預處理這個位置放某個字元會讓LCS長度怎麼轉移。

上面只是大概的思路,實現的時候有很多的細節,務必注意。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>


using std::cin;
using std::cout;
using std::endl;
using std::string;


const int mod = 1000000007
; int nLim; int nLenSrc; int nLenTar; int nLenBef[20]; int nLenAft[20]; int nTransSrc[20]; int nMap[26]; int nNxt[32780][4]; int nDiff[2][32780]; int ans[20]; string strSrc; void input() { cin >> strSrc >> nLenTar; } void init() { memset(nDiff, 0, sizeof(nDiff)); memset(ans, 0, sizeof(ans)
); nLenSrc = strSrc.size(); nLim = (1 << nLenSrc) - 1; for (register int i = 1; i <= nLenSrc; ++i) nTransSrc[i] = nMap[strSrc[i-1]-'A']; for (register int i = 0; i <= nLim; ++i) { memset(nLenBef, 0, sizeof(nLenBef)); for (register int j = 1; j <= nLenSrc; ++j) nLenBef[j] = nLenBef[j-1] + ((i>>j-1)&1); for (register int k = 0; k < 4; ++k) { for(register int j = 1; j <= nLenSrc; ++j) { nLenAft[j] = std::max(nLenAft[j-1], nLenBef[j]); if (nTransSrc[j] == k) nLenAft[j] = std::max(nLenAft[j], nLenBef[j-1] + 1); } nNxt[i][k] = 0; for (register int j = 1; j <= nLenSrc; ++j) if (nLenAft[j] ^ nLenAft[j-1]) nNxt[i][k] |= 1 << j - 1; } } } int calc(int x) { int ret = 0; while (x) { ret += x & 1; x >>= 1; } return ret; } void work() { nDiff[0][0] = 1; bool parity; for (register int i = 1; i <= nLenTar; ++i) { parity = i & 1; memset(nDiff[parity], 0, sizeof(nDiff[parity])); for (register int j = 0; j <= nLim; ++j) { for (register int k = 0; k < 4; ++k) { nDiff[parity][nNxt[j][k]] += nDiff[!parity][j]; nDiff[parity][nNxt[j][k]] %= mod; } } } for (register int t, i = 0; i <= nLim; ++i) { t = calc(i); ans[t] += nDiff[parity][i]; ans[t] %= mod; } } void output() { for (register int i = 0; i <= nLenSrc; ++i) { cout << ans[i] << endl; } } int main() { std::ios::sync_with_stdio(false); cout.tie(0); cin.tie(0); nMap['A'-'A'] = 0; nMap['C'-'A'] = 1; nMap['G'-'A'] = 2; nMap['T'-'A'] = 3; int nTestCases; cin >> nTestCases; for (register int i = 1; i <= nTestCases; ++i) { input(); init(); work(); output(); } return 0; }

寫了半天的簡單dp 沒救了