loj 2720 [NOI2018] 你的名字

loj 2720 [NOI2018] 你的名字

https://loj.ac/problem/2720

有一个串 \(S\) , \(Q\) 次询问,每次给出一个串 \(T\) ,和 \(l,r\) .

\(T\) 中有多少非空本质不同子串没有在 \(S[l \cdots r]\) 中出现过

\(|S| \le 5 \times 10^5, \sum |T| \le 10^6,1 \le l \le r \le |S|\)

Tutorial

https://dangxingyu.com/2018/10/30/noi2018-%E4%BD%A0%E7%9A%84%E5%90%8D%E5%AD%97/

https://www.cnblogs.com/cjyyb/p/10646874.html

考虑对于 \(T\) 建立SAM,想要对每个endpos等价类(节点)统计贡献.发现我们想要求出的,就是以其中 \(T\) 中每个位置作为结尾,在 \(S[l \cdots r]\) 中出现过的最长后缀的长度.

考虑通过在 \(S\) 的SAM上遍历以求得 \(Len_i\) 表示 \(T[1 \cdots i]\) 的最长的在 \(S[l \cdots r]\) 中出现过的后缀的长度.

\(l=1,r=n\) ,那么可以维护当前节点 \(u\) ,当前长度 \(now\) .设当前加入字符为 \(c=T_i\) ,那么若存在 \(ch_{u,c}\) 则前进,否则跳到 \(link_u\)

对于一般的情况,我们不仅要判断 \(ch_{u,c}\) 是否存在,还需要判断其代表的endpos等价类中出否出现过 \([l+now,r]\) 中的位置.所以此时不能直接跳 \(link\) ,应该每次 \(now\) 减1.

endpos等价类可以用线段树合并维护.

时间复杂度 \(O((|S| + \sum |T|) \log |S|)\)

Code

SAM中的link不是有序的.

线段树合并时,若还需要使用被合并的节点,那么需要新建一个节点返回.

#include <cassert>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#define debug(...) fprintf(stdout,__VA_ARGS__)
#define lson tree[u].ls,l,mid
#define rson tree[u].rs,mid+1,r
using namespace std;
template<class T> void read(T &x) {
	x=0; int f=1,ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=getchar();}
	x*=f;
}
typedef long long ll;
const int sigma=26;
const int maxn=5e5+50,maxm=1e6+50;
const int maxnode=maxm<<1;
int n; char S[maxn];
int m; char T[maxm];
int Q;
int Len[maxm];
int root[maxn<<1];
namespace seg {
	const int maxnode=maxn*100;
	int all;
	struct node {
		int ls,rs; 
	} tree[maxnode];
	void insert(int &u,int l,int r,int qp) {
		if(!u) u=++all;
		if(l==r) {
			return;
		}
		int mid=(l+r)>>1;
		if(qp<=mid) insert(lson,qp);
		else insert(rson,qp);
	}
	int merge(int a,int b) {
		if(!a||!b) return a+b;
		int c=++all;
		tree[c].ls=merge(tree[a].ls,tree[b].ls);
		tree[c].rs=merge(tree[a].rs,tree[b].rs);
		return c;
	}
	bool query(int u,int l,int r,int ql,int qr) {
		if(ql>qr) return 0;
		if(u==0) return 0;
		if(l==ql&&r==qr) {
			return 1;
		}
		int mid=(l+r)>>1;
		if(qr<=mid) return query(lson,ql,qr);
		else if(ql>mid) return query(rson,ql,qr);
		else {
			bool L=query(lson,ql,mid);
			bool R=query(rson,mid+1,qr);
			return L||R; 
		}
	}
}
vector<int> adj[maxn<<1];
struct string_suffix_machine {
	int las,all,len[maxnode],link[maxnode],endpos[maxnode],ch[maxnode][sigma];
	inline int newnode() {
		int u=++all;
		memset(ch[u],0,sizeof(ch[u]));
		return u;
	}
	void init() {
		all=las=0,link[0]=-1,endpos[0]=-1;
		memset(ch[0],0,sizeof(ch[0]));
	}
	void extend(int c,int pos) {
		int cur=newnode(),p=las;
		len[cur]=len[p]+1;
		endpos[cur]=pos;
		while(p!=-1&&!ch[p][c]) {
			ch[p][c]=cur;
			p=link[p];
		}
		if(p==-1) link[cur]=0;
		else {
			int q=ch[p][c];
			if(len[q]==len[p]+1) link[cur]=q;
			else {
				int nq=newnode();
				len[nq]=len[p]+1;
				link[nq]=link[q];
				endpos[nq]=pos;
				memcpy(ch[nq],ch[q],sizeof(ch[q]));
				link[cur]=link[q]=nq;
				while(p!=-1&&ch[p][c]==q) {
					ch[p][c]=nq;
					p=link[p];
				}
			}
		}
		las=cur;
	}
	void dfs(int u) {
		if(u!=0) seg::insert(root[u],0,n-1,endpos[u]);
		for(int i=0;i<adj[u].size();++i) {
			int v=adj[u][i];
			dfs(v);
			root[u]=seg::merge(root[u],root[v]);
		}
	}
	void build() {
		for(int i=1;i<=all;++i) {
			adj[link[i]].push_back(i);
		}
		dfs(0);
	}
	void travel(char *T,int m,int l,int r) {
		--l,--r;
		int u=0,now=0;
		for(int i=0;i<m;++i) {
			int c=T[i]-'a';
			while(true) {
				if(ch[u][c]&&seg::query(root[ch[u][c]],0,n-1,l+now,r)) {
					u=ch[u][c],++now;
					break;
				}
				else {
					if(u==0) {
						assert(now==0);
						break;
					}
					if(--now<=len[link[u]]) u=link[u];
				}
			}
			Len[i]=now;
		}
	}
	ll sol() {
		ll an=0;
		for(int i=1;i<=all;++i) {
			an+=max(0,len[i]-max(Len[endpos[i]],len[link[i]]));
		}
		return an;
	}
} sam0,sam1;
int main() {
	freopen("name.in","r",stdin);
	freopen("name.out","w",stdout);
	scanf("%s",S),n=strlen(S);
	sam0.init();
	for(int i=0;i<n;++i) sam0.extend(S[i]-'a',i);
	sam0.build();
	read(Q);
	while(Q--) {
		scanf("%s",T),m=strlen(T);
		int l,r; read(l),read(r);
		sam1.init();
		for(int i=0;i<m;++i) sam1.extend(T[i]-'a',i);
		sam0.travel(T,m,l,r);
		printf("%lld\n",sam1.sol());
	}
	return 0;
}
posted @ 2020-08-06 15:57  LJZ_C  阅读(73)  评论(0编辑  收藏  举报