「NOI2018」 你的名字

Description

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

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

Solution

首先我们考虑当\(l=1\)\(r=n\)的情况,那么我们考虑对于\(T\)的每一前缀求出一个最长的后缀满足这个后缀在\(S\)中出现过,然后记这个后缀的长度为\(len\)。那么如果不算本质不同的话,答案就是\(\frac{m\times (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\n", sam2.calc());
	}
	return 0;
}
posted @ 2020-06-10 16:35  Roal_L  阅读(88)  评论(0)    收藏  举报