hdu 5745 La Vie en rose DP + bitset优化
http://acm.hdu.edu.cn/showproblem.php?pid=5745
这题好劲爆啊。dp容易想,但是要bitset优化,就想不到了。
先放一个tle的dp。复杂度O(n * m)的
第一个串,记作str[],第二个记作sub[]
思路就是,设dp[i][j][k]表示,匹配了sub的前i个,以str的第j个结尾,然后sub[i]有三种状态,0表示不变化,1表示和前一个,2表示和后一个。
那么以求dp[i][j][0]为列
因为需要的是判断str的第j个结尾是否和sub的前i个完全匹配。
那么,要使得dp[i][j][0]为true,必然需要前i - 1个和j - 1个匹配成功。就是要递推过来。
那么dp[i][j][0] = (dp[i - 1][j - 1][0] || dp[i - 1][j - 1][1]) && (str[j] == sub[i])
其他的类似啦。
可以滚动下数组。
不清0的话,直接用DFN代替即可。这样的复杂度就是O(n * m),没有常数。但是还是TLE.
#include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> #include <assert.h> #define IOS ios::sync_with_stdio(false) using namespace std; #define inf (0x3f3f3f3f) typedef long long int LL; #include <iostream> #include <sstream> #include <vector> #include <set> #include <map> #include <queue> #include <string> #include <bitset> const int maxn = 1e5 + 20; char str[maxn], sub[maxn]; int dp[2][maxn][3]; int DFN; char ans[maxn]; void work() { DFN++; int lenstr, lensub; scanf("%d%d", &lenstr, &lensub); scanf("%s", str + 1); scanf("%s", sub + 1); // dp[0][0][0] = DFN; for (int i = 0; i <= lenstr; ++i) dp[0][i][0] = DFN; int now = 0; for (int i = 1; i <= lensub; ++i) { now = !now; for (int j = 1; j <= lenstr; ++j) { // if ((dp[i - 1][j - 1][0] || dp[i - 1][j - 1][1]) && str[j] == sub[i]) { // dp[i][j][0] = true; // } // if (dp[i - 1][j - 1][2] && str[j] == sub[i - 1]) { // dp[i][j][1] = true; // } // if ((dp[i - 1][j - 1][0] || dp[i - 1][j - 1][1]) && sub[i + 1] == str[j]) { // dp[i][j][2] = true; // } if ((dp[!now][j - 1][0] == DFN || dp[!now][j - 1][1] == DFN) && str[j] == sub[i]) { dp[now][j][0] = DFN + 1; } if (dp[!now][j - 1][2] == DFN && str[j] == sub[i - 1]) { dp[now][j][1] = DFN + 1; } if ((dp[!now][j - 1][0] == DFN || dp[!now][j - 1][1] == DFN) && str[j] == sub[i + 1]) { dp[now][j][2] = DFN + 1; } } DFN++; } for (int i = 1; i <= lenstr; ++i) { ans[i] = '0'; if (dp[now][i][0] == DFN || dp[now][i][1] == DFN || dp[now][i][2] == DFN) { // printf("1"); ans[i - lensub + 1] = '1'; } } ans[lenstr + 1] = '\0'; printf("%s\n", ans + 1); } int main() { #ifdef local freopen("data.txt", "r", stdin); // freopen("data.txt", "w", stdout); #endif int t; scanf("%d", &t); while (t--) work(); return 0; }
然后百度了一个bitset的优化。
考虑用bitset<1e5> dp[5000][k]
dp[i][k]
表示,处理了sub的前i个,当前sub的状态是k,然后匹配整个str串的结果就是dp[i][k],因为dp[i][k]是一个长度为1e5的01串嘛。也就是dp[i][k]就是匹配好的结果了。
那么,要快速计算dp[i][0],只需要(dp[i - 1][0] | dp[i - 1][1]) << 1
比如前2个匹配好的结果是"10"和"11"然后匹配成的第三位,就是"011",关键看看第三位,是1,也就是上面的dp[i - 1][j - 1][k] = true的意思。就是前面的匹配好了,但是还要看看str[j]和sub[i]的相等情况。
要想这一位是true,必然要这一位和sub对应相等。但是我们一次要算出整一个结果,而不是一个一个地算,那么,预处理个pos[26]的bitset就可以,&一下,就是要对应位置是1的,才是true。
3478ms,我是卡过去的
后来一直TLE
#include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> #include <assert.h> #define IOS ios::sync_with_stdio(false) using namespace std; #define inf (0x3f3f3f3f) typedef long long int LL; #include <iostream> #include <sstream> #include <vector> #include <set> #include <map> #include <queue> #include <string> #include <bitset> const int maxn = 1e5 + 20; bitset<maxn> dp[2][3]; char str[maxn], sub[maxn]; bitset<maxn>pos[26]; void init() { for (int i = 0; i <= 1; ++i) { for (int j = 0; j <= 2; ++j) { dp[i][j].reset(); } } for (int i = 0; i < 26; ++i) pos[i].reset(); } char ans[maxn]; void work() { init(); int lenstr, lensub; scanf("%d%d", &lenstr, &lensub); scanf("%s%s", str + 1, sub + 1); for (int i = 1; i <= lenstr; ++i) { pos[str[i] - 'a'][i] = 1; } dp[0][0] = pos[sub[1] - 'a']; if (lensub >= 2) dp[0][2] = pos[sub[2] - 'a']; int now = 0; for (int i = 2; i <= lensub; ++i) { now = !now; dp[now][0] = ((dp[!now][0] | dp[!now][1]) << 1) & pos[sub[i] - 'a']; dp[now][1] = (dp[!now][2] << 1) & pos[sub[i - 1] - 'a']; if (i < lensub) { dp[now][2] = ((dp[!now][0] | dp[!now][1]) << 1) & pos[sub[i + 1] - 'a']; } } for (int i = 1; i <= lenstr; ++i) { if (i + lensub - 1 > lenstr) { ans[i] = '0'; } else { if (dp[now][0][i + lensub - 1] || dp[now][1][i + lensub - 1]) { ans[i] = '1'; } else ans[i] = '0'; } } ans[lenstr + 1] = '\0'; printf("%s\n", ans + 1); // printf("\n"); } int main() { #ifdef local freopen("data.txt", "r", stdin); // freopen("data.txt", "w", stdout); #endif int t; scanf("%d", &t); while (t--) work(); return 0; }
posted on 2017-01-09 03:32 stupid_one 阅读(157) 评论(0) 编辑 收藏 举报