[BZOJ3214][Zjoi2013]丽洁体(hash+贪心)

Address

https://www.lydsy.com/JudgeOnline/problem.php?id=3214

Solution

为甚么做此题的人比 K 大数查询少……
首先把每个单词求下 hash ,只需要用 int 即可,不需要取膜。
然后在 T 中,从左往右贪心匹配,匹配出 T 的一个等于 A 的子序列,使子序列的右端点最靠左。设这个右端点为 L
同样地,也可以贪心匹配出 T 的一个等于 C 的子序列,使左端点尽量靠右,为 R
继续在 ( L , R ) 内匹配子序列 B ,使 B 的最左端点 l 和 最右端点 r 满足 r l 最小。
考虑到同一单词出现的次数 500 ,可以枚举 B 的左端点 l 后再贪心匹配出最小的 r
这样,复杂度就是 O ( | A | + | B | + | C | + c n t | T | )
c n t B 第一个字符在 T ( L , R ) 内的出现次数。

Code

注:单词的分隔符不一定是空格,也可能是小写字母以外的其他字符。被这个坑了好久

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
using namespace std;
const int N = 3e5 + 5, INF = 0x3f3f3f3f;
int L, R, nt, na, nb, nc, t[N], a[N], b[N], c[N], ans;
char T[N], A[N], B[N], C[N];
void __hash(char *s, int *rp, int &n, int len) {
    int i;
    For (i, 0, len - 1)
        if (s[i] < 'a' || s[i] > 'z') continue;
        else {
            if (i == 0 || s[i - 1] < 'a' || s[i - 1] > 'z') rp[++n] = 0;
            rp[n] = rp[n] * 27 + s[i] - 'a' + 1;
        }
}
int main() {
    int i, j, th = 1;
    gets(T); gets(A); gets(B); gets(C);
    __hash(T, t, nt, strlen(T));
    __hash(A, a, na, strlen(A));
    __hash(B, b, nb, strlen(B));
    __hash(C, c, nc, strlen(C));
    For (L, 1, nt)
        if (t[L] == a[th]) {
            if (th == na) {L++; break;}
            else th++;
        }
        else ans++;
    th = nc;
    Rof (R, nt, 1)
        if (t[R] == c[th]) {
            if (th == 1) {R--; break;}
            else th--;
        }
        else ans++;
    int minp = INF;
    For (i, L, R) {
        if (t[i] != b[1]) continue;
        th = 0; int mp = 0;
        For (j, i, R)
            if (t[j] == b[th + 1]) {
                th++;
                if (th == nb) break;
            }
            else mp++;
        if (th == nb) minp = min(minp, mp);
    }
    cout << ans + minp << endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/81168555