zoukankan      html  css  js  c++  java
  • CF666E Forensic Examination SAM+线段树合并+前缀树倍增

    $ color{#0066ff}{ 题目描述 }$

    给你一个串(S)以及一个字符串数组(T[1..m])(q)次询问,每次问(S)的子串(S[p_l..p_r])(T[l..r])中的哪个串里的出现次数最多,并输出出现次数。

    如有多解输出最靠前的那一个。

    (color{#0066ff}{输入格式})

    第一行一个个字符串表示(S)
    第二行一个数字(m)
    接下来(m)行每行一个字符串表示(T[i])
    接下来一行一个数字(q)表示询问次数
    接下来(q)行每行四个数字(l,r,ql,qr)表示一个询问

    (color{#0066ff}{输出格式})

    (q)行每行两个数字表示出现位置和次数

    (color{#0066ff}{输入样例})

    suffixtree
    3
    suffixtreesareawesome
    cartesiantreeisworsethansegmenttree
    nyeeheeheee
    2
    1 2 1 10
    1 3 9 10
    

    (color{#0066ff}{输出样例})

    1 1
    3 4
    

    (color{#0066ff}{数据范围与提示})

    (1<=∣s∣<=5*10^5,1<=m<=5*10^4,1<=q<=5*10^5)

    (color{#0066ff}{题解})

    考虑暴力,我们可以建立m个后缀自动机,然后暴力匹配, 复杂度是(O(nmq))

    这样无论如何都要枚举m个后缀自动机,复杂度是无法优化的

    我们考虑另一种思路,建立广义后缀自动机,每个节点记录一下属于哪个串bel,那么如果我们匹配之后到了某个点,我们需要在这个点的前缀树的子树内找到一个出现次数最多的bel且bel尽量小(靠左)

    也就是找子树最大值以及位置,我们考虑对前缀树上的每个点开一个动态开点线段树,维护子树内bel的最大值以及位置,这个刚开始可以先在extend中记录维护自己的东西,然后dfs前缀树,把子树内的线段树合并,这样每个点维护的就是子树内的线段树了

    于是对于一个询问,我们暴力匹配它的字串,跳到那个点,然后在那个点的线段树上查询就行了

    于是我们的复杂度由(O(nmq))降到了(O(q * n * logm))

    但是这样依然是过不了,时间浪费在了匹配上

    我们考虑优化匹配这个过程

    考虑在前缀树上倍增,因为每次跳父亲就是删去前面若干字符,所以我们可以先预处理出S的每个前缀在后缀自动机上的匹配情况,记录一下这个前缀的最长匹配后缀和匹配到的节点

    对于一个询问, 字串([l,r]),我们直接找到(S[1...r])的匹配节点,然后倍增往上跳,删去l前面的节点, 就是我们匹配的节点,然后再线段树查询就行了

    这样(O(q*logn*logm))就能过了

    #include<bits/stdc++.h>
    #define LL long long
    LL in() {
    	char ch; LL x = 0, f = 1;
    	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    	return x * f;
    }
    const int maxn = 7e5 + 100;
    struct Tree {
    	protected:
    		struct node {
    			node *ch[2];
    			int max, pos;
    			node(int max = 0, int pos = 0): max(max), pos(pos) { ch[0] = ch[1] = NULL; }
    			void upd() {
    				if(!ch[0] && !ch[1]) return;
    				if(!ch[0]) max = ch[1]->max, pos = ch[1]->pos;
    				else if(!ch[1]) max = ch[0]->max, pos = ch[0]->pos;
    				else { 
    					if(ch[0]->max >= ch[1]->max) max = ch[0]->max, pos = ch[0]->pos;
    					else max = ch[1]->max, pos = ch[1]->pos;
    				}
    			}
    		}*root[maxn];
    		int n;
    		void add(node *&o, int l, int r, int pos, int val) {
    			if(!o) o = new node();
    			if(l == r) return (void)(o->max += val, o->pos = l);
    			int mid = (l + r) >> 1;
    			if(pos <= mid) add(o->ch[0], l, mid, pos, val);
    			else add(o->ch[1], mid + 1, r, pos, val);
    			o->upd();
    		}
    		std::pair<int, int> query(node *o, int l, int r, int ql, int qr) {
    			if(!o) return std::make_pair(0, ql);
    			if(ql <= l && r <= qr) return std::make_pair(o->max, o->pos);
    			int mid = (l + r) >> 1;
    			if(qr <= mid) return query(o->ch[0], l, mid, ql, qr);
    			if(ql > mid) return query(o->ch[1], mid + 1, r, ql, qr);
    			std::pair<int, int> ansl = query(o->ch[0], l, mid, ql, qr);
    			std::pair<int, int> ansr = query(o->ch[1], mid + 1, r, ql, qr);
    			if(ansl.first >= ansr.first) return ansl;
    			return ansr;
    		}
    		node *merge(node *x, node *y, int l, int r) {
    			if(!x || !y) return x? x : y;
    			node *o = new node();
    			if(l == r) return o->max = x->max + y->max, o->pos = l, o;
    			int mid = (l + r) >> 1;
    			o->ch[0] = merge(x->ch[0], y->ch[0], l, mid);
    			o->ch[1] = merge(x->ch[1], y->ch[1], mid + 1, r);
    			return o->upd(), o;
    		}
    	public:
    		void set(int n) { this->n = n; }
    		void add(int id, int pos, int val) { add(root[id], 1, n, pos, val); }
    		std::pair<int, int> query(int id, int l, int r) { return query(root[id], 1, n, l, r); }
    		void merge(int x, int y) { root[x] = merge(root[x], root[y], 1, n); }
    }T;
    struct node {
    	node *ch[26], *fa;
    	std::vector<node *> treech;
    	int len;
    	node(int len = 0): len(len) { 
    		memset(ch, 0, sizeof ch); fa = NULL; treech.clear(); 
    	}
    }pool[maxn << 1], *tail, *lst, *root;
    void init() {
    	tail = pool;
    	root = lst = new(tail++) node();
    }
    void extend(int c, int id) {
    	node *o = new(tail++) node(lst->len + 1), *v = lst;
    	T.add(o - pool, id, 1);
    	for(; v && !v->ch[c]; v = v->fa) v->ch[c] = o;
    	if(!v) o->fa = root;
    	else if(v->len + 1 == v->ch[c]->len) o->fa = v->ch[c];
    	else {
    		node *n = new(tail++) node(v->len + 1), *d = v->ch[c];
    		std::copy(d->ch, d->ch + 26, n->ch);
    		n->fa = d->fa, d->fa = o->fa = n;
    		for(; v && v->ch[c] == d; v = v->fa) v->ch[c] = n;
    	}
    	lst = o;
    }
    int f[maxn << 1][22];
    void build() {
    	for(node *o = pool + 1; o != tail; o++) {
    		o->fa->treech.push_back(o);
    		f[o - pool][0] = o->fa - pool;
    	}
    }
    void dfs(node *o) {
    	for(node *to : o->treech) dfs(to), T.merge(o - pool, to - pool);
    }
    char s[maxn], ls[maxn];
    int len, m, matchmax[maxn], matchpos[maxn];
    void match() {
    	int nowlen = 0; node *o = root;
    	for(int i = 1; i <= len; i++) {
    		int p = s[i] - 'a';
    		if(o->ch[p]) o = o->ch[p], nowlen++;
    		else {
    			while(o != root && !o->ch[p]) o = o->fa, nowlen = o->len;
    			if(o->ch[p]) o = o->ch[p], nowlen++;
    		}
    		matchmax[i] = nowlen;
    		matchpos[i] = o - pool;
    	}
    }
    void getans(int ql, int qr, int l, int r) {
    	if(matchmax[r] < r - l + 1) return (void)(printf("%d %d
    ", ql, 0));
    	int x = matchpos[r];
    	for(int i = 20; i >= 0; i--) if((pool + f[x][i])->len >= r - l + 1) x = f[x][i];
    	std::pair<int, int> ans = T.query(x, ql, qr);
    	printf("%d %d
    ", ans.second, ans.first);
    }
    
    int main() {
    	scanf("%s", s + 1);
    	len = strlen(s + 1);
    	T.set(m = in()); init();
    	for(int i = 1; i <= m; i++) {
    		scanf("%s", ls);
    		lst = root;
    		for(char *p = ls; *p; p++) extend(*p - 'a', i);
    	}
    	build();
    	dfs(root);
    	match();
    	for(int j = 1; j <= 20; j++)
    		for(int i = 1; i <= tail - pool - 1; i++)
    			f[i][j] = f[f[i][j - 1]][j - 1];
    	int l, r, ql, qr;
    	for(int T = in(); T --> 0;) {
    		l = in(), r = in(), ql = in(), qr = in();
    		getans(l, r, ql, qr);
    	}
    	return 0;
    }
    
  • 相关阅读:
    fjnu2019第二次友谊赛 B题
    2019年FJNU低编赛 G题(dfs博弈)
    洛谷P2634 聪聪可可 (点分治)
    洛谷P3128 [USACO15DEC]最大流Max Flow (树上差分)
    Comet OJ
    Comet OJ
    Comet OJ
    Comet OJ
    Django models操作、中间件、缓存、信号、分页
    Ajax
  • 原文地址:https://www.cnblogs.com/olinr/p/10601205.html
Copyright © 2011-2022 走看看