zoukankan      html  css  js  c++  java
  • 「NOI2018」 你的名字

    Description

    给出一个字符串(S)和若干次询问

    每次询问给出一个字符串(T)(l,r),询问(T)中有多少个本质不同的子串在(S[l...r])中未出现过

    Solution

    首先我们考虑当(l=1)(r=n)的情况,那么我们考虑对于(T)的每一前缀求出一个最长的后缀满足这个后缀在(S)中出现过,然后记这个后缀的长度为(len)。那么如果不算本质不同的话,答案就是(frac{m imes (m-1)}{2}-sum len),但是题目要求本质不同,所以我们考虑对(T)也建出一个后缀自动机,然后每次就在(T)的后缀自动机上面每个节点维护一个(mx_i)表示(i)这个节点上所代表的字符串中所有长度(le mx_i)的串都在(S)中出现过,那么这样我们每次匹配到了一个(T)的一个前缀,我们就可以在代表这个前缀的节点上面打上一个标记,最后做一遍(DFS)合并所有标记就好了,答案就是(sum max(0,mx_i-len_{fa_i}))

    然后考虑(l,r)任意的情况,这样我们就需要将匹配的条件再进行限制,就是新限制匹配的这个点的(right)集合中一定存在点在([l,r])这个区间中,具体实现就是我们考虑线段树合并得出(right)集合,然后线段树上面的节点表示的就是在这个区间中(right)集合最大的位置,那么我们直接在这个点上面的线段树询问一下([1,r])的最大值并且将(len=min(len, ml - l + 1))(ml)表示最大值,这样限制匹配就好了

    Code

    //Created Time:2020年04月10日 星期五 20时27分30秒
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define N 1000006
    
    using namespace std;
    
    inline void ckmax(int &x, int y){x = x > y ? x : y;}
    
    int n;
    char s[N], t[N];
    
    //{{{SMT
    struct SMT{
    	int cnt;
    	int ls[N * 50], rs[N * 50], val[N * 50];
    
    	void update(int &pos, int l, int r, int p){
    		if(!pos) pos = ++cnt; ckmax(val[pos], p);
    		if(l == r) return ;
    		int mid = (l + r) >> 1;
    		if(mid >= p) update(ls[pos], l, mid, p);
    		else update(rs[pos], mid + 1, r, p);
    		return ;
    	}
    
    	int query(int &pos, int l, int r, int nl, int nr){
    		if(!pos) return 0;
    		if(l >= nl && r <= nr) return val[pos];
    		int mid = (l + r) >> 1, res = 0;
    		if(mid >= nl) ckmax(res, query(ls[pos], l, mid, nl, nr));
    		if(mid < nr) ckmax(res, query(rs[pos], mid + 1, r, nl, nr));
    		return res;
    	}
    
    	int merge(int x, int y){
    		if(!x || !y) return x | y;
    		int pos = ++cnt; val[pos] = max(val[x], val[y]);
    		ls[pos] = merge(ls[x], ls[y]);
    		rs[pos] = merge(rs[x], rs[y]);
    		return pos;
    	}
    }T;
    //}}}
    
    int rt[N];
    
    struct SAM1{
    	int cnt, lst;
    	int ch[N][26], fa[N], len[N], d[N], id[N];
    
    	void ins(int x, int pos){
    		int p = lst, np = ++cnt; lst = np; len[np] = len[p] + 1;
    		T.update(rt[np], 1, n, pos);
    		for(; p && !ch[p][x]; p = fa[p]) ch[p][x] = np;
    		if(!p) fa[np] = 1;
    		else{
    			int q = ch[p][x];
    			if(len[q] == len[p] + 1) fa[np] = q;
    			else{
    				int nq = ++cnt; memcpy(ch[nq], ch[q], sizeof ch[nq]);
    				len[nq] = len[p] + 1; fa[nq] = fa[q]; fa[q] = fa[np] = nq;
    				for(; p && ch[p][x] == q; p = fa[p]) ch[p][x] = nq;
    			}
    		}
    		return ;
    	}
    
    	void get(){
    		for(int i = 2; i <= cnt; ++i) ++d[len[i]];
    		for(int i = 1; i <= cnt; ++i) d[i] += d[i - 1];
    		for(int i = cnt; i >= 2; --i) id[d[len[i]]--] = i;
    		for(int i = cnt - 1; i; --i){
    			int u = id[i], f = fa[u];
    			rt[f] = T.merge(rt[f], rt[u]);
    		}
    		return ;
    	}
    
    	void find(int &p, int &nl, int x, int l, int r){
    		while(p){
    			int v = ch[p][x], ml = 0;
    			if(v) ml = T.query(rt[v], 1, n, 1, r);
    			if(ml >= l && ml - l + 1 > len[fa[p]]) 
    				return nl = min(nl + 1, ml - l + 1), p = v, void();
    			p = fa[p], nl = len[p];
    		} p = 1, nl = 0;
    		return ;
    	}
    }sam1;
    
    struct SAM2{
    	int cnt, lst;
    	int ch[N][26], fa[N], len[N], d[N], id[N], ans[N];
    
    	void init(){
    		for(int i = 1; i <= cnt; ++i){
    			memset(ch[i], 0, sizeof ch[i]);
    			fa[i] = len[i] = ans[i] = d[i] = 0;
    		}
    		cnt = lst = 1;
    		return ;
    	}
    
    	int ins(int x){
    		int p = lst, np = ++cnt; lst = np; len[np] = len[p] + 1;
    		for(; p && !ch[p][x]; p = fa[p]) ch[p][x] = np;
    		if(!p) fa[np] = 1;
    		else{
    			int q = ch[p][x];
    			if(len[q] == len[p] + 1) fa[np] = q;
    			else{
    				int nq = ++cnt; memcpy(ch[nq], ch[q], sizeof ch[nq]);
    				len[nq] = len[p] + 1; fa[nq] = fa[q]; fa[q] = fa[np] = nq;
    				for(; p && ch[p][x] == q; p = fa[p]) ch[p][x] = nq;
    			}
    		}
    		return np;
    	}
    
    	long long calc(){
    		long long res = 0;
    		for(int i = 2; i <= cnt; ++i) ++d[len[i]], res += len[i] - len[fa[i]];
    		for(int i = 1; i <= cnt; ++i) d[i] += d[i - 1];
    		for(int i = cnt; i >= 2; --i) id[d[len[i]]--] = i;
    		for(int i = cnt - 1; i; --i){
    			int u = id[i], f = fa[u];
    			res -= max(0, ans[u] - len[f]), ckmax(ans[f], min(len[f], ans[u]));
    		}
    		return res;
    	}
    
    }sam2;
    
    int pos[N];
    
    int main(){
    	scanf("%s", s + 1); n = strlen(s + 1); sam1.cnt = sam1.lst = 1;
    	for(int i = 1; i <= n; ++i) sam1.ins(s[i] - 'a', i);
    	sam1.get(); int T; cin >> T;
    	while(T--){
    		sam2.init(); scanf("%s", t + 1);
    		int m = strlen(t + 1), l, r;
    		scanf("%d%d", &l, &r);
    		for(int i = 1; i <= m; ++i)
    			pos[i] = sam2.ins(t[i] - 'a');
    		int now = 1, len = 0;
    		for(int i = 1; i <= m; ++i){
    			sam1.find(now, len, t[i] - 'a', l, r);
    			ckmax(sam2.ans[pos[i]], len);
    		}
    		printf("%lld
    ", sam2.calc());
    	}
    	return 0;
    }
    
  • 相关阅读:
    三数之和
    盛最多水的容器
    正则表达式匹配
    最长回文子串
    寻找两个有序数组的中位数
    2、二维数组中的查找
    1、找出数组中重复的数字
    mongodb的下载地址
    提取快捷方式的图标资源问题
    一条数据引发的问题
  • 原文地址:https://www.cnblogs.com/roal-l/p/13086220.html
Copyright © 2011-2022 走看看