zoukankan      html  css  js  c++  java
  • CF666E Forensic Examination

    题目链接

    CF666E Forensic Examination

    题目大意

    有一个串 (S)(m) 个串 (T_i)(q) 次询问,每次给出 (l,r,p_l,p_r),求 (max_{lleq ileq r}{freq(T_i,S[p_l,p_r])}),即 (S) 下标在 ([p_l,p_r]) 的子串在序号 (in[l,r]) 中的 (T_i) 的最大出现次数,输出满足最大次数的最小的下标 (i) 以及最大出现次数。

    (1leq|S|,qleq 5 imes10^5)(1leq m,sum|T_i|leq 5 imes 10^4)

    思路

    看到出现次数,果断后缀自动机(连样例里都写了是后缀数据结构),由于是要把 (S) 的子串放 (T_i) 里面算出现次数,显然把 (T_i) 拿出来建 (SAM) 会方便一些,有一个常见处理方式,我们将 (T_i) 串起来,之间用特殊字符相连构成一个大串 (T),然后对 (T)(SAM)

    注意到 (SAM) 上节点的右端点(集合)是固定的,而左端点管一段区间,那么这里也将 (S) 子串的右端点固定下来,对于每个右端点 (r),先预处理出 (S[0,r])(SAM) 上的最大匹配长度 (len_r) 和对应的状态 (st_r) 。首先如果询问的时候 (len_{p_r} < p_r-p_l+1),则 (S[p_l,p_r])(T) 上找不到对应的匹配,最大次数即为 (0)

    (len_{p_r}geq p_r-p_l+1) 的时候,我们尝试找到 (S[p_l,p_r]) 对应的状态,由于 (Parent;Tree) 到根的树链上的 (Max(s)) 是连续减小的,所以一直跳 (par(st)),直到 (Max(par(st))<p_r-p_l+1),此时必然有 (p_r-p_l+1in[Min(st),Max(st)])(st) 即为对应的状态,而向上跳的过程可以倍增预处理,做到 (O(nlogn))

    现在我们要求的就是在 (Right(st)) 里,([l,r]) 内哪个 (T_i) 对应的位置最多,这个显然可以用线段树维护,我们在 (SAM) 每一个节点上开一棵权值线段树,在建好 (Parent;Tree) 后自底向上更新 (Right) 信息,每次进行线段树合并即可,预处理 (O(nlogn))

    综上,时间复杂度为 (O((n+q)logn))

    实现细节

    • 由于每个节点的线段树后来都可能被访问到,所以要采用新建点的方式进行线段树合并,空间复杂度有 (2) 倍左右的常数,实际上要开的再大一点,开刚好 (2) 倍会 RE。

    • 本人对字符串匹配时的暴力跳 (fail) 做了一点优化,尽管实测没啥用,但还是说一下:

      对每个节点维护一个属性 (frt_sum),表示第一个 (Trans(s,c) eq null) 的祖先,这个在 (dfs) (Parent;Tree) 的时候自顶向下维护一下即可,这样匹配转移时可以做到严格 (O(1))

    • 有一个非常离谱的卡常,开始读入写的是 (cin) 去同步,后来改成了 (scanf) 和数字快读,效果图:

    (为什么可以快这么多)

    Code

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define mem(a,b) memset(a, b, sizeof(a))
    #define rep(i,a,b) for(int i = (a); i <= (b); i++)
    #define per(i,b,a) for(int i = (b); i >= (a); i--)
    #define N 505000
    #define T 50500
    #define LOG 14
    #define PII pair<int, int>
    #define fr first
    #define sc second
    #define t(x) SAM.t[x]
    using namespace std;
    
    inline int read(){
        int s = 0, w = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9'){ if(ch == '-') w = -1; ch = getchar(); }
        while(ch >= '0' && ch <= '9') s = (s<<3)+(s<<1)+(ch^48), ch = getchar();
        return s*w;
    }
    
    struct Segment_Tree{
        struct node{
            int ls, rs;
            PII mx;
            node(){ ls = rs = 0, mx = {0, 0}; }
        } t[4*T*LOG];
        int cnt;
        Segment_Tree(){ cnt = 0; }
    
        void update(int x){
            t[x].mx = max(t[t[x].ls].mx, t[t[x].rs].mx);
        }
    
        void insert(int &x, int pos, int l, int r){
            x = ++cnt;
            if(l == r){ t[x].mx = {1, -pos}; return; }
            int mid = (l+r)>>1;
            if(pos <= mid) insert(t[x].ls, pos, l, mid);
            else insert(t[x].rs, pos, mid+1, r);
            update(x);
        }
    
        inline int merge(int a, int b, int l, int r){
            if(!a || !b) return a+b;
            register int root = ++cnt;
            if(l == r){
                t[root].mx = t[a].mx, t[root].mx.fr += t[b].mx.fr;
            } else{
                int mid = (l+r)>>1;
                t[root].ls = merge(t[a].ls, t[b].ls, l, mid);
                t[root].rs = merge(t[a].rs, t[b].rs, mid+1, r);
                update(root);
            }
            return root;
        }
    
        inline PII query(int x, int l, int r, int left, int right){
            if(!x) return {-1, 0};
            if(l >= left && r <= right) return t[x].mx;
            register int mid = (l+r)>>1;
            register PII ret = {-1, 0};
            if(mid >= left) ret = max(ret, query(t[x].ls, l, mid, left, right));
            if(mid < right) ret = max(ret, query(t[x].rs, mid+1, r, left, right));
            return ret;
        }
    } SGT;
    
    int m;
    char s[N], t[T];
    
    struct Suffix_Automaton{
        struct state{
            int len, link, root, par[27];
            int next[27], up[LOG];
        } t[N<<1];
        int cnt, lst;
    
        void init(){ cnt = lst = 1; }
    
        void extend(int c, int id){
            int cur = ++cnt;
            t[cur].len = t[lst].len+1;
            if(id) SGT.insert(t[cur].root, id, 1, m);
            int u = lst;
            while(u && !t[u].next[c])
                t[u].next[c] = cur, u = t[u].link;
    
            if(u == 0) t[cur].link = 1;
            else{
                int q = t[u].next[c];
                if(t[q].len == t[u].len+1) t[cur].link = q;
                else{
                    int nq = ++cnt;
                    t[nq].len = t[u].len+1;
                    memcpy(t[nq].next, t[q].next, sizeof(t[nq].next));
                    t[nq].link = t[q].link;
                    t[q].link = t[cur].link = nq;
    
                    while(u && t[u].next[c] == q)
                        t[u].next[c] = nq, u = t[u].link;
                }
            }
            lst = cur;
        }
    } SAM;
    
    int head[N*2], to[2*N], nxt[2*N];
    int cnt;
    int state[N], len[N];
    
    void init(){ mem(head, -1), cnt = -1; }
    void add_e(int a, int b){
        nxt[++cnt] = head[a], head[a] = cnt, to[cnt] = b;
    }
    
    void dfs(int x){
        rep(c,0,26){
            if(!t(x).next[c]) t(x).par[c] = t(t(x).link).par[c];
            else t(x).par[c] = x;
        }
        t(x).up[0] = t(x).link;
        rep(i,1,13) t(x).up[i] = t(t(x).up[i-1]).up[i-1];
        for(int i = head[x]; ~i; i = nxt[i]){
            int y = to[i];
            dfs(y), t(x).root = SGT.merge(t(x).root, t(y).root, 1, m);
        }
    }
    
    int main(){
        scanf("%s %d", s, &m);
        SAM.init();
        rep(i,1,m){
            scanf("%s", t);
            int siz = strlen(t);
            rep(j,0,siz-1) SAM.extend(t[j]-'a', i);
            if(i != m) SAM.extend(26, 0);
        }
    
        init();
        rep(i,1,SAM.cnt) add_e(t(i).link, i);
        dfs(1);
    
        int st = 1, l = 0, n = strlen(s);
        rep(i,0,n-1){
            int c = s[i]-'a';
            if(t(st).next[c]) l++, st = t(st).next[c];
            else{
                if(t(st).par[c]){
                    st = t(st).par[c], l = t(st).len+1;
                    st = t(st).next[c];
                } else st = 1, l = 0;
            }
            len[i+1] = l, state[i+1] = st;
        }
    
        int q, r, pl, pr;
        q = read();
        while(q--){
            l = read(), r = read(), pl = read(), pr = read();
            if(len[pr] < pr-pl+1) printf("%d 0
    ", l);
            else{
                st = state[pr];
                per(i,13,0) if(t(t(st).up[i]).len >= pr-pl+1) st = t(st).up[i];
                PII ans = SGT.query(t(st).root, 1, m, l, r);
                if(ans.fr == -1) ans = {0, -l};
                printf("%d %d
    ", -ans.sc, ans.fr);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    《黑马程序员》 内存管理的认识(Objective
    《黑马程序员》 description方法(Objective
    《黑马程序员》 类的加载和初始化(Objective
    《黑马程序员》 category分类的使用(Objective
    《黑马程序员》 OC构造方法(Objective
    《黑马程序员》 OC编译器特性(Objective
    《黑马程序员》 OC的三大特性(Objective
    《黑马程序员》 OC的认识和第一个OC(Objective
    《黑马程序员》 extern与static的使用注意(C语言)
    《黑马程序员》 typedef的使用方法(C语言)
  • 原文地址:https://www.cnblogs.com/Neal-lee/p/14974605.html
Copyright © 2011-2022 走看看