【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 
#include 
#include 
#include 
#include 
#define fi first
#define se second
#define pii pair
#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
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
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();}
}

转载于:https://www.cnblogs.com/ivorysi/p/9580727.html

你可能感兴趣的:(数据结构与算法)