1. 程式人生 > 實用技巧 >LOJ2720. 「NOI2018」你的名字

LOJ2720. 「NOI2018」你的名字

給出一個字串\(S\),然後有若干次詢問,每次統計字串\(T\)中出現過的\(S_{l..r}\)中沒有出現過的子串的個數。

\(|S|,|T|\le 5*10^5,\sum |T|\le 10^6\)


自己想出來的做法:\(O(n\lg ^2 n)\),直接搞出SAM之後線段樹+樹上二分跳祖先。具體就是:對\(S\)建出SAM之後,離線列舉詢問右端點。主要問題是對\(T\)的每個字首求出作為\(S_{l..r}\)子串的最長字尾,即祖先中深度最大\(x\)滿足\(mxr_x-len_x+1\ge l\)。顯然可二分。

正解:\(O(n\lg n)\)。詳細一點說明(有些部分上面的做法也需要用到):

同樣是對\(S\)建出SAM,也是要對\(T\)的每個字首求出作為\(S_{l..r}\)子串的最長字尾。求出來之後去重即可(對\(T\)建SAM,建出\(T\)的fail樹,打標記表示其祖先不計入答案)。

對於這個問題,如果詢問\(S_{1..|S|}\)就可以直接在SAM上跑。現在限制了範圍,魔改一下跑的方式:(假設當前點為\(p\),匹配的長度為\(len\),新增的字元為\(c\)

原來:如果存在\(p.trans(c)\)\(p\)跳過去且\(len\leftarrow len+1\),否則跳\(fail\),且\(len\leftarrow p.fail.len\)。(細緻一些:否則\(len\leftarrow len-1\)

,如果\(len=p.fail\)\(p\)\(fail\)。時間複雜度不變。便於推廣)

魔改:處理出每個點的\(right\)集合。定義\(p.query(l,r)\)表示是否\(\exist x\in p.right,x\in [l,r]\)。如果\(p.trans(c).query(l+len,r)\)為真,則跳過去且\(len\leftarrow len+1\);否則\(len\leftarrow len-1\),如果\(len=p.fail\)則跳\(fail\)

可以如此說明它的正確性:當前已經配了合法的\(len\)長度,現在配\(len+1\),顯然\(p.trans(c).query(l+len,r)\)

為必要條件,並且根據\(right\)集合的定義得知這也是充分的。所以這是充分必要條件。

處理\(right\)集合可以用可持久化線段樹合併。


using namespace std;
#include <bits/stdc++.h>
#define N 500005
#define ll long long
int n,m;
char s[N],t[N];
struct SAM{
	struct Node{
		Node *c[26],*fa;
		int len;
	} d[N*2],*S,*T;
	int id(Node *x){return x-d;}
	int cnt;
	Node *newnode(){
		++cnt;
		memset(&d[cnt],0,sizeof d[cnt]);
		return &d[cnt];
	}
	void init(){
		T=S=&d[cnt=1];
		memset(&d[1],0,sizeof d[1]);
	}
	void insert(int ch){
		Node *nw=newnode(),*p,*q;
		nw->len=T->len+1;
		for (p=T;p && !p->c[ch];p=p->fa)
			p->c[ch]=nw;
		if (!p)
			nw->fa=S;
		else{
			q=p->c[ch];
			if (p->len+1==q->len)
				nw->fa=q;
			else{
				Node *clone=newnode();
				memcpy(clone,q,sizeof *q);
				clone->len=p->len+1;
				for (;p && p->c[ch]==q;p=p->fa)
					p->c[ch]=clone;
				nw->fa=q->fa=clone;
			}
		}
		T=nw;
	}
	void build(char *s){
		init();
		for (;*s;++s)
			insert(*s-'a');
	}
} S,T;
struct Seg{
	Seg *l,*r;
} d[N*50],*null,*rt[N*2];
int cnt;
Seg *newnode(){return &(d[++cnt]={null,null});}
void insert(int x,Seg *&t,int l=1,int r=n){
	if (t==null) t=newnode();
	if (l==r) return;
	int mid=l+r>>1;
	if (x<=mid) insert(x,t->l,l,mid);
	else insert(x,t->r,mid+1,r);		
}
Seg *merge(Seg *a,Seg *b){
	if (a==null) return b;
	if (b==null) return a;
	Seg *c=newnode();
	c->l=merge(a->l,b->l);
	c->r=merge(a->r,b->r);
	return c;
}
bool query(int st,int en,Seg *t,int l=1,int r=n){
	if (t==null) return 0;
	if (st<=l && r<=en) return 1;
	int mid=l+r>>1;
	bool res=0;
	if (st<=mid) res=query(st,en,t->l,l,mid);
	if (mid<en && !res) res=query(st,en,t->r,mid+1,r);
	return res;
}
struct EDGE{
	int to;
	EDGE *las;
} e[N*2];
int ne;
EDGE *last[N*2];
void link(int u,int v){
	e[ne]={v,last[u]};
	last[u]=e+ne++;
}
void dfsS(int x){
	for (EDGE *ei=last[x];ei;ei=ei->las){
		dfsS(ei->to);
		rt[x]=merge(rt[x],rt[ei->to]);
	}
}
void buildG(){
	S.build(s+1);
	null=d;
	*null={null,null};
	for (int i=1;i<=S.cnt;++i)
		rt[i]=null;
	SAM::Node *t=S.S;
	for (int i=1;i<=n;++i){
		t=t->c[s[i]-'a'];
		insert(i,rt[S.id(t)]);
	}
	for (int i=2;i<=S.cnt;++i)
		link(S.id(S.d[i].fa),i);
	dfsS(1);
}
int bz[N*2],dep[N*2];
void buildT(){
	T.build(t+1);
	memset(last,0,sizeof(EDGE*)*(T.cnt+1));
	ne=0;
	for (int i=2;i<=T.cnt;++i){
		link(T.id(T.d[i].fa),i);
		dep[i]=T.d[i].len;
		bz[i]=0;
	}
	bz[1]=0;
}
ll ans;
void dfsT(int x){
	for (EDGE *ei=last[x];ei;ei=ei->las){
		dfsT(ei->to);
		bz[x]=max(bz[x],bz[ei->to]);
		ans+=max(dep[ei->to]-max(bz[ei->to],dep[x]),0);
	}
}
int main(){
	freopen("name.in","r",stdin);
	freopen("name.out","w",stdout);
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	scanf("%s",s+1);
	n=strlen(s+1);
	buildG();
	int Q;
	scanf("%d",&Q);
	while (Q--){
		int l,r;
		scanf("%s%d%d",t+1,&l,&r);
		m=strlen(t+1);
		buildT();
		SAM::Node *q=S.S,*p=T.S;
		int len=0;
		for (int i=1;i<=m;++i){
			while (len && (!q->c[t[i]-'a'] || l+len>r || !query(l+len,r,rt[S.id(q->c[t[i]-'a'])]))){
				--len;
				if (len==q->fa->len)
					q=q->fa;
			}
			if (q->c[t[i]-'a'] && query(l+len,r,rt[S.id(q->c[t[i]-'a'])]))
				q=q->c[t[i]-'a'],len++;
			p=p->c[t[i]-'a'];
			bz[T.id(p)]=max(bz[T.id(p)],len);
		}
		ans=0;
		dfsT(1);
		printf("%lld\n",ans);
	}
	return 0;
}