【BZOJ】4560: [JLoi2016]字符串覆盖

题解

先用kmp求出来一个ed[i][j]表示在母串的第i位是第j个子串的结尾
考虑状压一个二进制位表示这个子串覆盖过没有

对于最大值,记一个dp[S][i]表示子串的使用状况为S,当前为母串的第i位,最大覆盖的个数
每次枚举S一个没有的子串j,把目标状态记成S^(1 << j - 1) = T
dp[T][i]可以从dp[T][i - 1]取个max过来,如果i还是当前枚举的子串j能匹配上,那么可以从dp[S][i - len[j]] + len[j]转移
然后就是考虑覆盖的情况,有些时候覆盖的情况并不存在,但是这个时候会比答案小,所以一定会被更新的
转移的方式是
dp[T][i] = max(dp[S][k] + i - k)我们使用单调队列来维护dp[S][k] - k即可

对于最小值,由于上面覆盖不存在的情况会比真正的情况要少,我们dp[S][i]的定义改成子串使用状况为S,i这个位置存在一个匹配串的结尾
转移方法同上,只不过需要额外一个数组记录一下S的前缀最小值
最小值的情况还会出现某个子串是另一个子串的子串,这个时候我们删掉被包含在另一个串里的串,将剩余的串作为目标状态取到min值

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
#define fi first
#define se second
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define enter putchar('\n')
#define space putchar(' ')
//#define ivorysi
using namespace std;
typedef long long int64;
template<class T>
void read(T &res) {
    res = 0;char c = getchar();T f = 1;
    while(c < '0' || c > '9') {
	if(c == '-') f = -1;
	c = getchar();
    }
    while(c >= '0' && c <= '9') {
	res = res * 10 + c - '0';
	c = getchar();
    }
    res *= f;
}
template<class T>
void out(T x) {
    if(x < 0) {putchar('-');x = -x;}
    if(x >= 10) out(x / 10);
    putchar('0' + x % 10);
}
char A[10005];
int N,nxt[10005],len[5],L;
char str[5][1005];
int dp[(1 << 4) + 5][10005],sum[10005],que[10005],ql,qr;
bool ed[10005][5],isalone[5];

void Init() {
    scanf("%s",A + 1);
    read(N);
    for(int i = 1 ; i <= N ; ++i) scanf("%s",str[i] + 1);
    L = strlen(A + 1);
    memset(ed,0,sizeof(ed));memset(isalone,1,sizeof(isalone));
    for(int i = 1 ; i <= N ; ++i) len[i] = strlen(str[i] + 1);
    for(int i = 1 ; i <= N ; ++i) {
	for(int j = 2 ; j <= len[i] ; ++j) {
	    int p = nxt[j - 1];
	    while(p && str[i][p + 1] != str[i][j]) p = nxt[p];
	    if(str[i][p + 1] == str[i][j]) nxt[j] = p + 1;
	    else nxt[j] = 0;
	}
	int p = 0;
	for(int k = 1 ; k <= L ; ++k) {
	    while(p && str[i][p + 1] != A[k]) p = nxt[p];
	    if(str[i][p + 1] == A[k]) ++p;
	    else p = 0;
	    if(p == len[i]) {ed[k][i] = 1;p = nxt[p];}
	}
	for(int j = 1 ; j <= N ; ++j) {
	    if(len[i] == len[j]) continue;
	    p = 0;
	    for(int k = 1 ; k <= len[j] ; ++k) {
		while(p && str[i][p + 1] != str[j][k]) p = nxt[p];
		if(str[i][p + 1] == str[j][k]) ++p;
		else p = 0;
		if(p == len[i]) {isalone[i] = 0;break;}
	    }
	}
    }
}
void SolveMax() {
    memset(dp,0,sizeof(dp));
    for(int S = 0 ; S < (1 << N) ; ++S) {
	for(int i = 1 ; i <= N ; ++i) {
	    if(!(S & (1 << i - 1))) {
		que[ql = qr = 1] = 0;
		int T = S ^ (1 << i - 1);
		for(int k = 1 ; k <= L ; ++k) {
		    while(ql <= qr && que[ql] <= k - len[i]) ++ql;
		    while(ql <= qr && dp[S][que[qr]] - que[qr] <= dp[S][k] - k) --qr;
		    que[++qr] = k;
		    dp[T][k] = max(dp[T][k - 1],dp[T][k]);
		    if(ed[k][i]) {
			dp[T][k] = max(dp[T][k],dp[S][k - len[i]] + len[i]);
			if(ql <= qr) dp[T][k] = max(dp[T][k],dp[S][que[ql]] + k - que[ql]);
		    }
		}
	    }
	}
    }
    out(dp[(1 << N) - 1][L]);enter;
}
void SolveMin() {
    memset(dp,0,sizeof(dp));
    for(int S = 0 ; S < (1 << N) ; ++S) for(int k = 0 ; k <= L ; ++k) dp[S][k] = L;
    dp[0][0] = 0;
    for(int S = 0 ; S < (1 << N) ; ++S) {
	for(int i = 1 ; i <= N ; ++i) {
	    if(!(S & (1 << i - 1))) {
		que[ql = qr = 1] = 0;
		int T = S ^ (1 << i - 1);
		sum[0] = dp[S][0];
		for(int k = 1 ; k <= L ; ++k) {
		    while(ql <= qr && que[ql] <= k - len[i]) ++ql;
		    while(ql <= qr && dp[S][que[qr]] - que[qr] >= dp[S][k] - k) --qr;
		    que[++qr] = k;
		    if(ed[k][i]) {
			dp[T][k] = min(dp[T][k],sum[k - len[i]] + len[i]);
			if(ql <= qr) dp[T][k] = min(dp[T][k],dp[S][que[ql]] + k - que[ql]);
		    }
		    sum[k] = min(sum[k - 1],dp[S][k]);
		}
	    }
	}
    }
    int S = 0;
    for(int i = 1 ; i <= N ; ++i) if(isalone[i]) S |= (1 << i - 1);
    int ans = L;
    for(int k = 1 ; k <= L ; ++k) ans = min(ans,dp[S][k]);
    out(ans);space;
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    int T;
    read(T);
    while(T--) {Init();SolveMin();SolveMax();}
}
posted @ 2018-09-04 15:46  sigongzi  阅读(291)  评论(0编辑  收藏  举报