[NOI2018]你的名字,洛谷P4770,SAM

正题

      建议先阅读且会做这些题再来做这道题

      如果不用去重,不用考虑区间[l,r]?

      我们让T在S所建成的SAM上跑,失配直接跳fail,那么我们可以获得一个T的前缀[1,i]的最大匹配长度,设为p[i],那么我们将i-p[i]加起来即可.

      如果不用去重,考虑区间[l,r]?

      我们对于SAM上的每个节点,用线段树合并来维护出这个点的right集合.一个点可以走到它的后继,若当前匹配长度为len,那么我们看看它的后继的right集合中[l+len,r]是否有点,若有,那么就跳上去,否则将len--,再判断,若不在当前点的管理范围内,则跳fail,i-p[i]加起来即可.

      如果要去重,要考虑区间[l,r]?

      我们先用上面一问的方法求出p[i],再将T构造SAM,上面的每个点代表的就是不同的串,所以我们只需要看看每个点是否有长度>p[right[i]]的字符串,加起来就好,这个right[i]可以是这个点的right集合的任意一个元素,选哪个元素显然不对当前点的贡献产生影响.

      当然,这也是我第一次用struct来写SAM,不是很懂,代码不好看,见谅

#include<bits/stdc++.h>
using namespace std;

const int N=1000010;

struct Suffix_Automaton{
	int ch[N][26],tot,fail[N],rig[N],las,len[N],n,a[N],sum[N];
	char s[N];
	void init(int x){
		for(int i=0;i<26;i++) ch[x][i]=0;
		fail[x]=len[x]=0;rig[x]=1e9; 
	}
	void pre(){init(0);fail[0]=-1;tot=las=0;n=strlen(s+1);}
	void extend(int pos,int c){
		int x=++tot,p=las;init(x);
		las=x;rig[x]=pos;len[x]=len[p]+1;
		while(p!=-1 && !ch[p][c]) ch[p][c]=x,p=fail[p];
		if(p==-1) fail[x]=0;
		else if(len[ch[p][c]]==len[p]+1) fail[x]=ch[p][c];
		else{
			int q=++tot,tmp=ch[p][c];init(q);len[q]=len[p]+1;
			fail[q]=fail[tmp];fail[tmp]=fail[x]=q;
			for(int i=0;i<26;i++) ch[q][i]=ch[tmp][i];
			while(p!=-1 && ch[p][c]==tmp) ch[p][c]=q,p=fail[p];
		}
	}
	void build(){
		scanf("%s",s+1);pre();
		for(int i=1;i<=n;i++) s[i]-='a',extend(i,s[i]),sum[i]=0;
		for(int i=1;i<=tot;i++) sum[len[i]]++;
		for(int i=n-1;i>=1;i--) sum[i]+=sum[i+1];
		for(int i=1;i<=tot;i++) a[sum[len[i]]--]=i;
	}
}S,T;
int ls[20000010],rs[20000010];
int rt[N],num,sum[N],a[N],q,p[N];

void insert(int&now,int x,int l=1,int r=S.n){
	if(!now) now=++num;
	if(l==r) return ;
	int mid=(l+r)/2;
	if(x<=mid) insert(ls[now],x,l,mid);
	else insert(rs[now],x,mid+1,r);
}

int merge(int x,int y,int l=1,int r=S.n){
	if(!x || !y) return x+y;
	int mid=(l+r)/2,now=++num;
	ls[now]=merge(ls[x],ls[y],l,mid);
	rs[now]=merge(rs[x],rs[y],mid+1,r);
	return now;
}

bool gs(int now,int x,int y,int l=1,int r=S.n){
	if(!now) return false;
	if(x==l && y==r) return true;
	int mid=(l+r)/2;
	if(y<=mid) return gs(ls[now],x,y,l,mid);
	else if(mid<x) return gs(rs[now],x,y,mid+1,r);
	else return gs(ls[now],x,mid,l,mid)|gs(rs[now],mid+1,y,mid+1,r);
}

void solve(int l,int r){
	int now=0,len=0,bound;
	for(int i=1;i<=T.n;i++){
		while(now!=-1){
			if(S.ch[now][T.s[i]]){
				bound=(now==0?-1:S.len[S.fail[now]]);
				while(len>bound){
					if(l+len<=r && gs(rt[S.ch[now][T.s[i]]],l+len,r)){
						break;
					}
					len--;
				}
				if(len!=bound) break;
			}
			now=S.fail[now],len=S.len[now];
		}
		if(now==-1) len=now=0,p[i]=0;
		else len++,now=S.ch[now][T.s[i]],p[i]=len;
	}
	long long ans=0;
	for(int i=1;i<=T.tot;i++){
		int tmp=T.a[i];
		T.rig[T.fail[tmp]]=min(T.rig[T.fail[tmp]],T.rig[tmp]);
		ans+=max(T.len[tmp]-max(T.len[T.fail[tmp]],p[T.rig[tmp]]),0);
	}
	printf("%lld\n",ans);
}

int main(){
	S.build();
	for(int i=1;i<=S.tot;i++){
		if(S.rig[S.a[i]]!=1e9) insert(rt[S.a[i]],S.rig[S.a[i]]);
		rt[S.fail[S.a[i]]]=merge(rt[S.fail[S.a[i]]],rt[S.a[i]]);
	}
	scanf("%d",&q);
	int l,r;
	while(q--){
		T.build();scanf("%d %d",&l,&r);
		solve(l,r);
	}
}

猜你喜欢

转载自blog.csdn.net/Deep_Kevin/article/details/108455254