[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; }
博主蒟蒻,随意转载。但必须附上原文链接:http://www.cnblogs.com/NaVi-Awson/,否则你会终生找不到妹子!!!