zoukankan      html  css  js  c++  java
  • 「十二省联考 2019」字符串问题「后缀数组」

    「十二省联考 2019」字符串问题

    题意:

    给定一个字符串 (S),并选取 (n_a) 个子串作为 (A) 类串,选取 (n_b) 个子串作为 (B) 类串,还给了 (m) 个支配关系:第 (x)(A) 类串支配第 (y)(B) 类串。

    请你求出最长串 (T) 的长度,(T) 满足 (T = A_{p_1}+A_{p_2}+...+A_{p_k}),且 (forall iin[1,k-1],A_{p_i}) 支配某个串 (B_q),而 (B_q)(A_{p_{i+1}}) 的前缀。

    题解:后缀数组 + 主席树优化建图

    下面用 (n) 表示 (|S|)( ext{suffix[x]}) 表示 (S(x,n)) 这个后缀。

    40 pts

    我们可以建出图论模型:(A) 类和 (B) 类串都当做点,(A) 类串向其支配的 (B) 类串连边,(B) 类串向满足该串是 (A) 前缀

    (A) 连边,用 hash 完成这一过程。(A) 类点权是其长度,(B) 类点权为 (0)。先拓扑排序,有环输出 -1,然后按拓扑序 dp 求最长路径即可。

    该算法缺点在于边数过大,边数是 (O(m + n_a n_b)) 的。

    80 pts

    (O(m)) 可以接受而 (O(n_a n_b)) 不能接受,考虑使用数据结构优化建图,优化 (B)(A) 连那部分。

    我们考虑若一个 (B) 类串 (S(l,r)) 可以连向一个 (A) 类串 (S(x,y)),当且仅当 ( ext{LCP}( ext{suffix}[l], ext{suffix}[x]) geq r - l + 1)(y-x+1geq r-l+1),即 以 (A) 左端点为起点的后缀 和 以 (B) 左端点为起点的后缀的 LCP 至少是 (B) 的长度,且 (A) 的长度不短于 (B)。80pts 的测试点满足 (|A_i|geq |B_j|),第二个条件直接忽略。我们考虑建后缀数组,这样 (B) 所连的 (A) 会在后缀数组的一段区间中。因此使用线段树优化建图,每次二分出区间左右端点,(B) 向区间连边,线段树底层对应点向 (A) 连边。然后再拓扑 dp。

    点数 (O(n_a+n_b+n)),边数 (O(m + n + n_a + n_b log n))

    100 pts

    现在要把长度条件加进去。一看相当于是个矩形连边,可以使用 KD-Tree 优化建图,不一定能过。

    有个还算不错的做法是把 (A,B) 各自按长度从大到小排序,这样加入 (B_i) 之前把 (|A|geq |B_i|) 的都加入数据结构,然后 (B_i) 连向数据结构中的一个区间。很容易发现这个数据结构用主席树最适合,树上父子边直接移到我们的图里,加入 (A) 的时候就建一个新版本,别忘了新版本对应结点连向旧版本,叶子连向 (A)(B) 连边就连向表示对应区间的一些结点,这样 (B) 可以走到包括当前版本在内的历史版本的叶结点。

    点数 (O(n_a log n + n_b)),边数 (O(m + (n_a + n_b)log n))。空间限制1GB,建议数组尽量开大。

    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #define pii pair<int, int>
    #define fs first
    #define sc second
    #define rep(i, j, k) for(int i = j; i <= k; ++ i)
    #define per(i, j, k) for(int i = j; i >= k; -- i)
    typedef long long ll;
    using namespace std;
    const int N = 2e5 + 10, NN = N * 30, MM = N * 100;
    int n, sa[N], rk[N], cnt[N], t[N];
    int h[N], lg[N], st[N][20];
    char s[N];
    void build() {
       int c = *max_element(s + 1, s + n + 1);
       fill(cnt + 1, cnt + c + 1, 0);
       rep(i, 1, n) cnt[rk[i] = s[i]] ++;
       rep(i, 1, c) cnt[i] += cnt[i - 1];
       rep(i, 1, n) sa[cnt[s[i]] --] = i;
       for(int num = 0, k = 1; k <= n; k <<= 1, c = num, num = 0) {
          rep(i, n - k + 1, n) t[++ num] = i;
          rep(i, 1, n) if(sa[i] > k) t[++ num] = sa[i] - k;
          fill(cnt + 1, cnt + c + 1, 0);
          rep(i, 1, n) cnt[rk[i]] ++;
          rep(i, 1, c) cnt[i] += cnt[i - 1];
          per(i, n, 1) sa[cnt[rk[t[i]]] --] = t[i];
          copy(rk + 1, rk + n + 1, t + 1); rk[sa[1]] = num = 1;
          rep(i, 2, n) {
             int u = sa[i - 1] + k <= n ? t[sa[i - 1] + k] : 0;
             int v = sa[i] + k <= n ? t[sa[i] + k] : 0;
             if(!(t[sa[i - 1]] == t[sa[i]] && u == v)) ++ num;
             rk[sa[i]] = num;
          }
          if(num == n) break ;
       }
       int k = 0;
       rep(i, 1, n) {
          if(rk[i] == 1) { h[1] = k = 0; continue ; }
          if(k) k --;
          int u = sa[rk[i] - 1];
          for(; i + k <= n && u + k <= n && s[i + k] == s[u + k]; k ++) ;
          h[rk[i]] = k;
       }
       lg[1] = 0;
       rep(i, 2, n) lg[i] = lg[i >> 1] + 1;
       rep(i, 1, n) st[i][0] = h[i];
       rep(j, 1, lg[n]) rep(i, 1, n - (1 << j) + 1)
          st[i][j] = min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
    }
    int lcp(int x, int y) {
       if(x == y) return n - sa[x] + 1;
       x ++; int k = lg[y - x + 1];
       return min(st[x][k], st[y - (1 << k) + 1][k]);
    }
    pii search(int x, int len) {
       int l = 1, r = x, mid; pii ans(0, 0);
       while(l <= r) {
          mid = (l + r) >> 1;
          if(lcp(mid, x) >= len) r = (ans.fs = mid) - 1;
          else l = mid + 1;
       }
       l = x; r = n;
       while(l <= r) {
          mid = (l + r) >> 1;
          if(lcp(x, mid) >= len) l = (ans.sc = mid) + 1;
          else r = mid - 1;
       }
       return ans;
    }
    
    struct Edge { int v, nxt; } e[MM];
    int na, nb, rt[N], Naid[N], Nbid[N], NaL[N], NbL[N];
    int m, ec, hd[NN], idx, ls[NN], rs[NN];
    pii Na[N], Nb[N];
    
    void clr(int n) { fill(hd + 1, hd + n + 1, -1); ec = 0; }
    int app() { hd[idx + 1] = -1; return ++ idx; }
    void add(int u, int v) { e[ec] = (Edge) {v, hd[u]}; hd[u] = ec ++; }
    
    void update(int p, int &u, int l, int r, int x, int A) {
       u = app();
       if(p) add(u, p);
       if(l == r) { ls[u] = rs[u] = 0; add(u, A); return ; }
       int mid = (l + r) >> 1;
       if(x <= mid) rs[u] = rs[p], update(ls[p], ls[u], l, mid, x, A);
       else ls[u] = ls[p], update(rs[p], rs[u], mid + 1, r, x, A);
       if(ls[u]) add(u, ls[u]);
       if(rs[u]) add(u, rs[u]);
    }
    void addedge(int u, int l, int r, int ql, int qr, int from) {
       if(!u) return ;
       if(l == ql && r == qr) { add(from, u); return ; }
       int mid = (l + r) >> 1;
       if(qr <= mid) return addedge(ls[u], l, mid, ql, qr, from);
       if(ql > mid) return addedge(rs[u], mid + 1, r, ql, qr, from);
       addedge(ls[u], l, mid, ql, mid, from);
       addedge(rs[u], mid + 1, r, mid + 1, qr, from);
    }
    int deg[NN];
    ll pa[NN];
    ll solve() {
       fill(deg + 1, deg + idx + 1, 0);
       fill(pa + 1, pa + idx + 1, 0);
       rep(i, 0, ec - 1) deg[e[i].v] ++;
       static int q[NN]; int ql = 0, qr = 0;
       rep(i, 1, idx) if(!deg[i]) {
          q[qr ++] = i; pa[i] = (i <= na ? NaL[i] : 0);
       }
       int cnt = 0;
       while(ql < qr) {
          int u = q[ql ++]; cnt ++;
          for(int i = hd[u]; ~ i; i = e[i].nxt) {
             int v = e[i].v;
             pa[v] = max(pa[v], pa[u] + (v <= na ? NaL[v] : 0));
             if(! --deg[v]) {
                q[qr ++] = v;
             }
          }
       }
       return cnt == idx ? *max_element(pa + 1, pa + idx + 1) : -1;
    }
    bool cmpa(int x, int y) { return NaL[x] > NaL[y]; }
    bool cmpb(int x, int y) { return NbL[x] > NbL[y]; }
    int main() {
       int test; scanf("%d", &test);
       rep(T, 1, test) {
          scanf("%s", s + 1); n = strlen(s + 1); build();
          scanf("%d", &na);
          rep(i, 1, na) scanf("%d%d", &Na[i].fs, &Na[i].sc), Naid[i] = i, NaL[i] = Na[i].sc - Na[i].fs + 1;
          sort(Naid + 1, Naid + na + 1, cmpa);
          scanf("%d", &nb);
          rep(i, 1, nb) scanf("%d%d", &Nb[i].fs, &Nb[i].sc), Nbid[i] = i, NbL[i] = Nb[i].sc - Nb[i].fs + 1;
          sort(Nbid + 1, Nbid + nb + 1, cmpb);
          clr(idx = na + nb);
          scanf("%d", &m);
          rep(i, 1, m) { int u, v; scanf("%d%d", &u, &v); add(u, na + v); }
          int pos = 1; rt[0] = 0;
          rep(i, 1, nb) {
             int B = Nbid[i];
             for(; pos <= na && NaL[ Naid[pos] ] >= NbL[B]; ++ pos) {
                update(rt[pos - 1], rt[pos], 1, n, rk[ Na[ Naid[pos] ].fs ], Naid[pos]);
             }
             pii p = search(rk[Nb[B].fs], NbL[B]);
             addedge(rt[pos - 1], 1, n, p.fs, p.sc, na + B);
          }
          printf("%lld
    ", solve());
       }
       return 0;
    }
    
  • 相关阅读:
    Mac下配置Android adb环境变量
    在远程Linux上搭建jenkins
    早期渲染模板-Thymeleaf总结
    启动SpringBoot时的banner自定义修改
    MySQL密码策略及修改或忘记密码
    (转)Maven使用总结
    转-安全层开发—Shiro对比Spring Security
    JavaWeb应用-发布打成jar包和war包区别
    Gitea中常规git工作流程
    简述JSP与Servlet的区别及联系
  • 原文地址:https://www.cnblogs.com/hongzy/p/13136761.html
Copyright © 2011-2022 走看看