HDU_4295
可以用f[i][j][k]表示递推到S的第i个字符时,选取的字符串的集合为k(用二进制表示),从当前字符开始向后已经覆盖了长度j的字符。
在这里不妨只讨论最小覆盖长度,最大覆盖长度类似。为了方便dp所以选用刷表的方式,首先令f[i][0][0]为0,接着对于任意一个f[i][j][k],实际上有两种决策,一种是当前这个位置什么也不放就过度到下一个字符,那么就有f[i+1][j-1][k]=std::min(f[i+1][j-1][k],f[i][j][k]),另一种就是从当前位置开始放上字符串t,这时要稍稍讨论一下,因为字符串t的长度可能会影响状态j,也就是从当前字符开始向后已经覆盖了的长度,设放上字符串t后的j的状态为nj,f的值nf,那么就有f[i][nj][k|1<<t]=std::min(f[i][nj][k|1<<t],nf)。
为了能够方便的知道当前位置能否放上字符串t,就需要预处理出从S的各个位置开始是否能够放上a、b、c、d四个字符串,这里暴力也行KMP也行,反正这部分的复杂度对整体的复杂度来讲影响不大。
#include<stdio.h> #include<string.h> #include<algorithm> #define MAXL 4110 #define INF 0x3f3f3f3f #define inf 0xc3c3c3c3 char s[MAXL], b[4][70]; int N, P[70], h[4][MAXL], len[4], f[MAXL][70][16]; void KMP(int k) { int i, j; P[1] = 0; for(i = 2, j = 0; i <= len[k]; i ++) { while(j && b[k][j + 1] != b[k][i]) j = P[j]; if(b[k][j + 1] == b[k][i]) ++ j; P[i] = j; } memset(h[k], 0, sizeof(h[k][0]) * (N + 1)); for(i = 1, j = 0; i <= N; i ++) { while(j && b[k][j + 1] != s[i]) j = P[j]; if(b[k][j + 1] == s[i]) ++ j; if(j == len[k]) h[k][i - len[k] + 1] = 1, j = P[j]; } } void init() { int i, j, k; N = strlen(s + 1); for(i = 0; i < 4; i ++) scanf("%s", b[i] + 1), len[i] = strlen(b[i] + 1), KMP(i); } void dp(int c, const int &(&fun) (const int &, const int &)) { int i, j, k, nj, nf, ans = c ? INF : inf; memset(f, c ? 0x3f : 0xc3, sizeof(f[0]) * (N + 1)); for(i = 1; i <= N; i ++) { f[i][0][0] = 0; for(j = 0; j <= 64; j ++) { for(k = 0; k < 15; k ++) if(f[i][j][k] != (c ? INF : inf)) { int nj = j > 0 ? j - 1 : 0; f[i + 1][nj][k] = fun(f[i + 1][nj][k], f[i][j][k]); for(int t = 0; t < 4; t ++) if((k & 1 << t) == 0 && h[t][i]) { nj = std::max(j, len[t]); f[i][nj][k | 1 << t] = fun(f[i][nj][k | 1 << t], f[i][j][k] + nj - j); } } ans = fun(ans, f[i][j][15]); } } printf("%d", ans); } void solve() { dp(1, std::min), printf(" "), dp(0, std::max), printf("\n"); } int main() { while(scanf("%s", s + 1) == 1) { init(); solve(); } return 0; }