hdu KMP + DP
题意 :hdu 4295给出一个母串,长度不超过4096, 两外有4个字串(都是母串的字串),这四个串可以覆盖到母串中,当然可以重叠,现在想求最多与最小能覆盖多少个字符。
思路: 这道题是一个看起来不难的题,但是其实却很难的题。 首先预处理出字串能否放到第i个字符的位置。这里可以用KMP预处理。 用数组can[i][j]表示。
接下来是一个DP, 其实最大和最小是一个思想,这里一最小为例, dp[i][j][k],表示处理到第i个字符,k表示状态,表示用了那一个字串,从当前位置还以往后放j个字符的答案。
面对每一个状态,一共有两种选择,一种是不放,继续往前走,那么
dp[i+1][j-1][k] = min(dp[i+1][j-1][k], dp[i][j][k])
还有另外一种选择就是重新放一个字串,那么这时间的方程式 t = max(j,len[m]);
dp[i+1][t][k|(1<<m)] = min(dp[i+1][t][k|(1<<m)],
dp[i][j][k]-j+len[m]);
但是这道题有一个比较坑爹的地方, 就是卡常数, 在更新的时候要判断一下dp[i][j][k]!=INF,在更新,否则就会tle
AC代码:
View Code
#include <cstdio> #include <string> #include <cstring> #include <iostream> #include <queue> using namespace std; const int N = 4110, BN = 70, INF = 1<<29; bool can[4][N]; int len[4], dpx[N][BN][20],dpn[N][BN][20]; int next[N]; char ss[N], s[4][BN]; void getcan(char *s, int lc, bool *can) { next[0]=-1; int j=-1; for(int i=1; i<lc; i++) { while(j != -1 && s[i] != s[j+1]) j = next[j]; if(s[i] == s[j+1]) j++; next[i]=j; } int len = strlen(ss+1); j = -1; for(int i=1; i<=len; i++) { while(j!=-1 && ss[i]!=s[j+1]) j = next[j]; if(ss[i] == s[j+1]) { j++; if(j == lc-1) { can[i-j] = 1; j=next[j]; } } else j=-1; } } void init() { memset(can, 0, sizeof(can)); for(int i=0; i<4; i++) { scanf("%s", s[i]); len[i] = strlen(s[i]); getcan(s[i], len[i], can[i]); } } int min(int a, int b) { return a > b ? b:a; } int max(int a, int b) { return a > b ? a:b; } void DP1() { int n = strlen(ss+1), ans = INF, t; for(int i=0; i<=n; i++) { for(int j=0; j<=64; j++) { for(int k=0; k<16; k++) { dpn[i][j][k] = INF; } } dpn[i][0][0] = 0; } for(int i=1; i<=n; i++) { for(int j=0; j<=64; j++) { for(int k=0; k<15; k++) if(dpn[i][j][k] != INF) { if(j) dpn[i+1][j-1][k] = min(dpn[i+1][j-1][k], dpn[i][j][k]); else dpn[i+1][j][k] = min(dpn[i+1][j][k], dpn[i][j][k]); for(int m=0; m<4; m++) { if( ((1<<m)&k) || (!can[m][i]) ) continue; t = max(j, len[m]); dpn[i][t][k|(1<<m)] = min (dpn[i][t][k|(1<<m)],dpn[i][j][k]-j+t); } } ans = min(ans,dpn[i][j][15]); } } printf("%d ",ans); } void DP2() { int n = strlen(ss+1), ans = -INF, t; for(int i=0; i<=n; i++) { for(int j=0; j<=64; j++) { for(int k=0; k<16; k++) { dpx[i][j][k] = -INF; } } dpx[i][0][0] = 0; } for(int i=1; i<=n; i++) { for(int j=0; j<=64; j++) { for(int k=0; k<15; k++) if(dpx[i][j][k] != -INF) { if(j) dpx[i+1][j-1][k] = max(dpx[i+1][j-1][k], dpx[i][j][k]); else dpx[i+1][j][k] = max(dpx[i+1][j][k], dpx[i][j][k]); for(int m=0; m<4; m++) { if( ((1<<m)&k) || (!can[m][i]) ) continue; t = max(j, len[m]); dpx[i][t][k|(1<<m)] = max (dpx[i][t][k|(1<<m)],dpx[i][j][k]-j+t); } } ans = max(ans, dpx[i][j][15]); } } printf("%d\n", ans); } int main() { while(scanf("%s",ss+1) == 1) { init(); DP1(); DP2(); } return 0; }


浙公网安备 33010602011771号