[ZJOI 2013]丽洁体

Description

题库链接

给出四个字符串 \(T,A,B,C\) ,问你至少在 \(T\) 串中删去几个单词能使得 \(T\) 串变为 \(A?B?C\) 的形式,其中 \(?\) 表示任意多的单词,可以为空。

\(1\leq |T|,|A|,|B|,|C|\leq 50000\)

Solution

首先注意到不同的字符串最多有 \(O(26^5)\) 个。我们可以先把字符串 \(hash\) ,方便处理。

其次,容易发现的是对于要满足 \(A,C\) 串的要求,直接贪心就好了,分别从串前和尾扫一遍。

我们考虑如何选 \(B\) 。我们先把 \(T\) 串中 \(A,C\) 相关的部分去掉。

一个显然的 \(DP\) 是记 \(f_{i,j}\) 为处理过的 \(T\) 串前 \(i\) 位选中了 \(B\) 串前 \(j\) 位要删去的最少单词数。但这样转移是 \(O(|T||B|)\) 的,能拿 \(70pts\)

转换思路。

\(f_i\) 表示第 \(i\) 位的字符最晚在 \(T\) 串的哪个位置出现; \(g_i\) 表示匹配前 \(i\) 位最少删除的字符。

注意到相同的字符最多只有 \(500\) ,我们可以暴力枚举所有的在 \(B\) 串中与 \(T_i\) 相同的位置。这样复杂度为 \(O(500|T|)\)

当然了,如果用相同的思想,枚举所有的在 \(B\) 串中与 \(T_i\) 相同的位置。用线段树优化 \(70pts\) 是可以做到 \(O(500|T|log_2 |B|)\)

Code

#include <bits/stdc++.h>
using namespace std;
const int N = 250000, M = 12356630, inf = 2e9;

char T[N+5], A[N+5], B[N+5], C[N+5];
int t[N+5], a[N+5], b[N+5], c[N+5], tt, ta, tb, tc, L, R, ans;
int f[N+5], g[N+5];
vector<int>s[M+5];
void get(char *ch, int *a, int &tot) {
    int len = 0; char c;
    while ((c = getchar()) != '\n') ch[++len] = c;
    for (int i = 1, j; i <= len; i = j+1) {
    int sum = 0; j = i;
    while (ch[j] >= 'a' && ch[j] <= 'z') sum = sum*26+ch[j]-'a'+1, ++j;
    a[++tot] = sum;
    }
}
void work() {
    get(T, t, tt);
    get(A, a, ta);
    get(B, b, tb);
    get(C, c, tc);
    for (int i = 1, loc = 1; i <= tt; i++) {
    if (t[i] == a[loc]) ++loc;
    else ans++;
    if (loc == ta+1) {L = i+1; break; }
    }
    for (int i = tt, loc = tc; i >= 1; i--) {
    if (t[i] == c[loc]) --loc;
    else ans++;
    if (loc == 0) {R = i-1; break; }
    }
    int sum = inf;
    memset(g, 127/3, sizeof(g));
    for (int i = 1; i <= tb; i++) s[b[i]].push_back(i);
    for (int i = L; i <= R; i++)
    for (int j = s[t[i]].size()-1; j >= 0; j--) {
        int x = s[t[i]][j];
        if (x == 1) f[x] = i, g[x] = 0;
        else if (f[x-1]) f[x] = i, g[x] = g[x-1]+i-f[x-1]-1;
        sum = min(sum, g[tb]);
    }
    printf("%d\n", sum+ans);
}
int main() {work(); return 0; }

转载于:https://www.cnblogs.com/NaVi-Awson/p/8672734.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值