[十二省联考2019]字符串问题 题解

传送门

写篇题解记录一下我第一道sa大题。我再也不是不会sa的sb了

其实自从去年12月份决定放弃冲省队开始就一直没有按原计划继续学下去(废话),所以各种后缀数据结构一片空白,结果在考场上遇到了这个题。当时我就知道这题肯定是用后缀数据结构优化匹配,然后用线段树优化建图,但是我并不能写得出任何一个可用的后缀数据结构…

其实不是什么很难的东西啦…只是代码有点长。

首先考虑一个40分暴力:对于每一组支配关系 ( x , y ) (x,y) ,从 A x A_x B y B_y 连边;用字符串哈希判断如果 B i B_i A j A_j 的前缀,从 B i B_i A j A_j 连边。这两类边分别是 O ( m ) O(m) O ( n a n b ) O(n_an_b) 的。然后跑DAG最长路即可。

这里的复杂度主要来源于第二类数量为 O ( n a n b ) O(n_an_b) 的边。我们求出后缀数组以及 h e i g h t \mathrm{height} 数组,那么对于一个 B i = ( l b i , r b i ) B_i=(\mathrm{lb}_i,\mathrm{rb}_i) ,可以找到最小的 l l 和最大的 r r ,满足 l c p { s a l , s a l + 1 ,   , s a r } r b i l b i + 1 \mathrm{lcp}\{\mathrm{sa}_l,\mathrm{sa}_{l+1},\cdots,\mathrm{sa}_r\}\geq \mathrm{rb}_i-\mathrm{lb}_i+1 ,那么所有满足 r a n k l a j [ l , r ] \mathrm{rank}_{\mathrm{la}_j}\in[l,r] A A 类串 A j A_j 都包含 B i B_i 作为前缀,因此我们从 B i B_i 向区间 [ l , r ] [l,r] 连边。这个连边可以用线段树优化。

可是这样写只有 80 80 分。问题在于,这样做就没有考虑到 A j < B i |A_j|<|B_i| 的情况。我们可以按 A A 类串的长度从大到小建立可持久化线段树,在对应的树上连边。这个过程我借鉴了一下小粉兔大佬的写法,将所有的 A A B B 串混在一起按长度排序,长度相同的让 A A 串在前,这样的话直接扫一遍,碰到 A A 串就建新树,碰到 B B 串就在当前树上连边即可。

然后DAG最长路的求法多种多样。我一开始的naive做法是先判环,没环的话再跑记忆化搜索;然而判环我又一直写不对,于是我就直接上了Tarjan,然后常数瞬间变大。。。不过幸好时限够长没有什么问题。事实上可以直接在记忆化搜索的时候判环,就像NOIP2017day1t3那样写。

#include <cctype>
#include <cstdio>
#include <climits>
#include <algorithm>
#include <cstring>
#include <vector>

template <typename T> inline void read(T& x) {
    int f = 0, c = getchar(); x = 0;
    while (!isdigit(c)) f |= c == '-', c = getchar();
    while (isdigit(c)) x = x * 10 + c - 48, c = getchar();
    if (f) x = -x;
}
template <typename T, typename... Args>
inline void read(T& x, Args&... args) {
    read(x); read(args...); 
}
template <typename T> void write(T x) {
    if (x < 0) x = -x, putchar('-');
    if (x > 9) write(x / 10);
    putchar(x % 10 + 48);
}
template <typename T> inline void writeln(T x) { write(x); puts(""); }
template <typename T> inline bool chkmin(T& x, const T& y) { return y < x ? (x = y, true) : false; }
template <typename T> inline bool chkmax(T& x, const T& y) { return x < y ? (x = y, true) : false; }

typedef long long LL;

const int maxn = 2e5 + 207;

char s[maxn];
int sa[maxn], rank[maxn], height[maxn];
int tmp[maxn], tax[maxn];
int st[30][maxn], lg[maxn];
int la[maxn], ra[maxn], lb[maxn], rb[maxn];
int kase, n, na, nb, m, sigma;

struct Sub {
    int rk, len, pos;
    bool tp;
    Sub() : rk(0), len(0), pos(0), tp(0) {}
    Sub(int r, int l, int p, bool t) : rk(r), len(l), pos(p), tp(t) {}
};
Sub sub[maxn << 1];
inline bool operator<(const Sub &lhs, const Sub &rhs) {
    return lhs.len == rhs.len ? (int)lhs.tp < rhs.tp : lhs.len > rhs.len;
}

inline void rsort() {
    std::fill(tax, tax + sigma + 1, 0);
    for (int i = 1; i <= n; ++i) ++tax[rank[i]];
    for (int i = 1; i <= sigma; ++i) tax[i] += tax[i - 1];
    for (int i = n; i; --i) sa[tax[rank[tmp[i]]]--] = tmp[i];
}
inline void getsa() {
    sigma = 26;
    for (int i = 1; i <= n; ++i)
        rank[i] = s[i] - 'a' + 1, tmp[i] = i;
    rsort();
    for (int w = 1, p = 0; p < n; sigma = p, w <<= 1) {
        int cnt = 0;
        for (int i = 1; i <= w; ++i) tmp[++cnt] = n - w + i;
        for (int i = 1; i <= n; ++i) if (sa[i] > w) tmp[++cnt] = sa[i] - w;
        rsort(); std::swap(tmp, rank);
        rank[sa[1]] = p = 1;
        for (int i = 2; i <= n; ++i)
            rank[sa[i]] = tmp[sa[i]] == tmp[sa[i - 1]] && tmp[sa[i] + w] == tmp[sa[i - 1] + w] ? p : ++p;
    }
}
inline void getheight() {
    for (int i = 1, k = 0; i <= n; ++i) {
        if (rank[i] == 1) { height[rank[i]] = k = 0; continue; }
        if (k) --k;
        int j = sa[rank[i] - 1];
        while (i + k <= n && j + k <= n && s[i + k] == s[j + k]) ++k;
        height[rank[i]] = k;
    }
    for (int i = 2; i <= n; ++i) {
        lg[i] = lg[i >> 1] + 1;
        st[0][i] = height[i];
    }
    for (int j = 1; 1 << j <= n; ++j) {
        for (int i = 1; i <= 1 << j; ++i) st[j][i] = 0;
        for (int i = 1 << j | 1; i <= n; ++i)
            st[j][i] = std::min(st[j - 1][i], st[j - 1][i - (1 << (j - 1))]);
    }
}

std::vector<int> G[maxn << 5];
inline void ae(int x, int y) { G[x].push_back(y); }

LL f[maxn << 5];
bool vis[maxn << 5], in[maxn << 5];

LL dfs(int x) {
    if (in[x]) return -1;
    if (vis[x]) return f[x];
    in[x] = 1;
    LL self = x > na ? 0 : ra[x] - la[x] + 1;
    f[x] = self;
    for (auto v : G[x]) {
        LL ret = dfs(v);
        if (ret == -1) return -1;
        chkmax(f[x], self + ret);
    }
    in[x] = 0;
    vis[x] = 1;
    return f[x];
}

struct Node {
    int lc, rc;
    Node() : lc(0), rc(0) {}
};
Node T[maxn << 5];
int root[maxn], tot;

inline void clear() {
    for (int i = 1; i <= tot; i += 4) {
        T[i].lc = 0; T[i + 1].lc = 0; T[i + 2].lc = 0; T[i + 3].lc = 0;
        T[i].rc = 0; T[i + 1].rc = 0; T[i + 2].rc = 0; T[i + 3].rc = 0;
    }
    for (int i = 1; i <= tot; i += 4) {
        f[i] = 0; f[i + 1] = 0; f[i + 2] = 0; f[i + 3] = 0;
        vis[i] = 0; vis[i + 1] = 0; vis[i + 2] = 0; vis[i + 3] = 0;
        in[i] = 0; in[i + 1] = 0; in[i + 2] = 0; in[i + 3] = 0;
    }
    for (int i = 1; i <= tot; i += 4) {
        G[i].clear(); G[i + 1].clear(); G[i + 2].clear(); G[i + 3].clear();
    }
    for (int i = 1; i <= na; i += 4) {
        root[i] = 0; root[i + 1] = 0; root[i + 2] = 0; root[i + 3] = 0;
    }
}

void modify(int &o, int l, int r, int pos, int a) {
    T[++tot] = T[o];
    if (o) ae(tot, o);
    o = tot;
    if (l == r) { ae(o, a); return; }
    int mid = (l + r) >> 1;
    if (pos <= mid) {
        modify(T[o].lc, l, mid, pos, a);
        ae(o, T[o].lc);
    } else {
        modify(T[o].rc, mid + 1, r, pos, a);
        ae(o, T[o].rc);
    }
}
void addedge(int o, int lb, int rb, int u, int l, int r) {
    if (!o || l > rb || r < lb) return;
    if (l <= lb && r >= rb) { ae(u, o); return; }
    int mid = (lb + rb) >> 1;
    addedge(T[o].lc, lb, mid, u, l, r);
    addedge(T[o].rc, mid + 1, rb, u, l, r);
}

int main() {
    read(kase);
    while (kase--) {
        scanf("%s", s + 1); n = strlen(s + 1);
        getsa(); getheight();
        read(na);
        for (int i = 1; i <= na; ++i) {
            read(la[i], ra[i]);
            sub[i] = Sub(rank[la[i]], ra[i] - la[i] + 1, i, 0);
        }
        read(nb);
        for (int i = 1; i <= nb; ++i) {
            read(lb[i], rb[i]);
            sub[na + i] = Sub(rank[lb[i]], rb[i] - lb[i] + 1, i, 1);
        }
        std::sort(sub + 1, sub + na + nb + 1);
        tot = na + nb;
        for (int i = 1, o = 0; i <= na + nb; ++i) {
            if (sub[i].tp) {
                int lb = sub[i].rk, rb = sub[i].rk;
                for (int j = lg[sub[i].rk - 1]; ~j; --j)
                    if (st[j][lb] >= sub[i].len) lb -= 1 << j;
                for (int j = lg[n - sub[i].rk]; ~j; --j)
                    if (rb + (1 << j) <= n && st[j][rb + (1 << j)] >= sub[i].len) rb += 1 << j;
                addedge(root[o], 1, n, na + sub[i].pos, lb, rb);
            } else {
                ++o;
                modify(root[o] = root[o - 1], 1, n, sub[i].rk, sub[i].pos);
            }
        }
        read(m);
        for (int i = 1, x, y; i <= m; ++i)
            read(x, y), ae(x, na + y);
        bool circle = 0;
        for (int i = 1; i <= tot && !circle; ++i)
            if (!vis[i]) if (dfs(i) == -1) circle = 1;
        if (circle) puts("-1");
        else writeln(*std::max_element(f + 1, f + tot + 1));
        clear();
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39677783/article/details/89787039