• CF 666E Forensic Examination 【SAM 倍增 线段树合并】


    CF 666E Forensic Examination 

    题意:

    给出一个串(s)(n)个串(t_i)(q)次询问,每次询问串(s)的子串(s[p_l:p_r])在串(t_l)(t_r)中哪个串中出现次数最多,以及出现次数最多的哪个串的下标

    题解:

    考虑把(n)(t)串建出广义后缀自动机,然后后缀自动机上每个节点用动态开点线段树来维护每个(t)串能匹配到的数量,把每个(t)串的每个后缀能匹配的最长的串对应的后缀自动机上的点以当前(t)串的下标在当前点的线段树上加一,然后做线段树合并

    对于串(s),记录以每个位置为右端点的能在自动机上匹配的最长子串长度,以及在自动机上对应的点,用类似做(LCS)的的办法来做,就是不断跳(link)来到达匹配的点

    对于每次询问,找到自动机上对应(s[p_l:p_r])的节点,这个可以从以(s[p_r])为右端点的最长匹配串对应的自动机上的点不断跳(parent)树找到,可以用倍增来优化,然后直接查询区间最大值和最大值下标即可

    要注意(s[p_l:p_r])在自动机上无匹配点的情况,特判一下即可

    view code
    //#pragma GCC optimize("O3")
    //#pragma comment(linker, "/STACK:1024000000,1024000000")
    #include<bits/stdc++.h>
    using namespace std;
    function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
    const int MAXN = 5e5+7;
    struct SegmentTree{
        int tot, ls[MAXN<<5], rs[MAXN<<5], root[MAXN];
        pair<int,int> maxx[MAXN<<5];
        SegmentTree(){ memset(root,0,sizeof(root)); tot = 0; }
        int newnode(){ tot++; ls[tot] = rs[tot] = 0; maxx[tot] = make_pair(0,0); return tot; }
        void pushup(int rt){
            if(maxx[ls[rt]].first>=maxx[rs[rt]].first) maxx[rt] = maxx[ls[rt]];
            else maxx[rt] = maxx[rs[rt]];
        }
        void modify(int &rt, int pos, int L, int R, int x){
            if(!rt) rt = newnode();
            if(L + 1 == R){
                maxx[rt].first += x;
                maxx[rt].second = L;
                return;
            }
            int mid = (L + R) >> 1;
            if(pos < mid) modify(ls[rt],pos,L,mid,x);
            else modify(rs[rt],pos,mid,R,x);
            pushup(rt);
        }
        pair<int,int> ask(int L, int R, int l, int r, int rt){
            if(!rt or l>=R or L>=r) return make_pair(0,MAXN);
            if(L<=l and r<=R) return maxx[rt];
            int mid = (l + r) >> 1;
            auto p1 = ask(L,R,l,mid,ls[rt]);
            auto p2 = ask(L,R,mid,r,rs[rt]);
            if(p1.first>=p2.first) return p1;
            else return p2;
        }
        int merge(int u, int v, int L, int R){
            if(!u or !v) return u | v;
            if(L + 1 == R){
                maxx[u].first += maxx[v].first;
                return u;
            }
            int mid = (L + R) >> 1;
            ls[u] = merge(ls[u],ls[v],L,mid);
            rs[u] = merge(rs[u],rs[v],mid,R);
            pushup(u);
            return u;
        }
    };
    struct Trie{
        int tot, ch[MAXN][26];
        Trie():tot(0){ memset(ch,0,sizeof(ch)); }
        void insert(const char *s){
            int u = 0, n = strlen(s);
            for(int i = 0; i < n; i++){
                int c = s[i] - 'a';
                if(!ch[u][c]) ch[u][c] = ++tot;
                u = ch[u][c];
            }
        }
    };
    struct Suffix_automaton{
        Trie trie;
        SegmentTree seg;
        int SEGTREE_SIZE;
        int tot, ch[MAXN][26], pos[MAXN], link[MAXN], par[MAXN][20], len[MAXN];
        vector<int> G[MAXN];
        Suffix_automaton(){ tot = 0; link[tot] = -1; }
        int extend(int c, int last){
            int p = last, np = ++tot;
            len[np] = len[last] + 1;
            while(p!=-1 and !ch[p][c]) ch[p][c] = np, p = link[p];
            if(p==-1) link[np] = 0;
            else{
                int q = ch[p][c];
                if(len[p]+1==len[q]) link[np] = q;
                else{
                    int clone = ++tot;
                    len[clone] = len[p] + 1;
                    link[clone] = link[q];
                    memcpy(ch[clone],ch[q],sizeof(ch[q]));
                    while(p!=-1 and ch[p][c]==q) ch[p][c] = clone, p = link[p];
                    link[q] = link[np] = clone;
                }
            }
            return np;
        }
        void dfs(int u){
            par[u][0] = link[u];
            for(int i = 1; ~par[u][i-1]; i++) par[u][i] = par[par[u][i-1]][i-1];
            for(int v : G[u]) dfs(v);
        }
        void build(){
            pos[0] = 0;
            queue<int> que;
            que.push(0);
            while(!que.empty()){
                int u = que.front(); que.pop();
                for(int c = 0; c < 26; c++){
                    if(!trie.ch[u][c]) continue;
                    int v = trie.ch[u][c];
                    pos[v] = extend(c,pos[u]);
                    que.push(v);
                }
            }
            for(int i = 1; i <= tot; i++) G[link[i]].push_back(i);
            memset(par,255,sizeof(par));
            dfs(0);
        }
        void modify(string &s, int id){
            int u = 0;
            for(int i = 0; i < (int)s.length(); i++){
                int c = s[i] - 'a';
                u = ch[u][c];
                seg.modify(seg.root[u],id,1,SEGTREE_SIZE+1,1);
            }
        }
    }sam;
    struct Query{
        int l, r, qid;
        Query(){}
        Query(int _l, int _r, int id):l(_l),r(_r),qid(id){}
    };
    vector<Query> Q[MAXN];
    char s[MAXN], buf[MAXN];
    int n, m, sampos[MAXN], matlen[MAXN], l;
    pair<int,int> ret[MAXN];
    
    vector<string> vec_s;
    void dfsMerge(int u){
        for(int v : sam.G[u]){
            dfsMerge(v);
            sam.seg.root[u] = sam.seg.merge(sam.seg.root[u],sam.seg.root[v],1,sam.SEGTREE_SIZE+1);
        }
        for(auto qs : Q[u]){
            auto p = sam.seg.ask(qs.l,qs.r+1,1,sam.SEGTREE_SIZE+1,sam.seg.root[u]);
            if(p.first==0) ret[qs.qid] = make_pair(0,qs.l);
            else ret[qs.qid] = p;
        }
    }
    void solve(){
        scanf("%s %d",s + 1,&n);
        l = strlen(s + 1);
        sam.SEGTREE_SIZE = n;
        vec_s.resize(n);
        for(int i = 0; i < n; i++){
            scanf("%s",buf);
            vec_s[i] = string(buf);
            sam.trie.insert(buf);
        }
        sam.build();
        for(int i = 0; i < n; i++) sam.modify(vec_s[i], i+1);
        int cur = 0, mat = 0;
        for(int i = 1; i <= l; i++){
            int c = s[i] - 'a';
            if(sam.ch[cur][c]) cur = sam.ch[cur][c], mat++;
            else{
                while(cur!=-1 and !sam.ch[cur][c]) cur = sam.link[cur];
                if(cur==-1) cur = 0, mat = 0;
                else mat = sam.len[cur] + 1, cur = sam.ch[cur][c];
            }
            sampos[i] = cur;
            matlen[i] = mat;
        }
        scanf("%d",&m);
        for(int i = 1; i <= m; i++){
            int l, r, pl, pr;
            scanf("%d %d %d %d",&l,&r,&pl,&pr);
            int lth = pr - pl + 1;
            int p = sampos[pr];
            if(matlen[pr]<lth) ret[i] = make_pair(0,l);
            else{
                for(int j = 19; ~j; j--) if(sam.par[p][j]!=-1 and sam.len[sam.par[p][j]]>=lth) p = sam.par[p][j];
                Q[p].push_back(Query(l,r,i));
            }
        }
        dfsMerge(0);
        for(int i = 1; i <= m; i++) cout << ret[i].second << ' ' << ret[i].first << endl;
    }
    int main(){
        #ifndef ONLINE_JUDGE
        freopen("Local.in","r",stdin);
        // freopen("ans.out","w",stdout);
        #endif
        solve();
        return 0;
    }
    
  • 相关阅读:
    Doc2Vec -- "tag '23943' not seen in training corpus/invalid" 错误
    一行代码书写的神奇
    MySQL8.0-Public Key Retrieval is not allowed
    Dubbo-admin-2.7上下(新旧)版本打包发布到Liunx服务器
    Git遇到SSL错误:fatal: unable to access 'https://***************': OpenSSL SSL_read: Connection was reset, errno 10054
    Google浏览器快捷键
    Windows快捷键
    IDEA快捷键
    LocalDateTime
    数组
  • 原文地址:https://www.cnblogs.com/kikokiko/p/13981820.html
Copyright © 2020-2023  润新知