【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();}
}