【CF1037H】Security(贪心)(SAM)(线段树)

传送门


题解:

NOI2018 你的名字 是一个套路。

首先我们求出 t t t s s s中的匹配位置。然后贪心考虑尽可能让答案前缀和 T T T相同。

也就是说我们要在某个匹配后面接一个比 T T T当前位置大的字符,然后看能不能在 [ l , r ] [l,r] [l,r]中匹配上。

线段树合并维护一下right集合。然后看能否匹配直接看right集合在这个区间是否为空即可。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	inline char gc(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int get_s(char *s){
		int len=0;char c;
		while(isspace(c=gc()));
		while(s[len++]=c-'a',!isspace(c=gc())&&c!=EOF);
		return s[len]='\0',len;
	}
	
	template<typename T>
	inline T get(){
		char c;T num;
		while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;

cs int N=1e5+7;

char s[N];int n;
char t[N<<1];int tl;

namespace SGT{
	cs int N=::N*40;
	int lc[N],rc[N],tot;
	inline int _copy(int u){++tot,lc[tot]=lc[u],rc[tot]=rc[u];return tot;}
	inline void ins(int &u,int l,int r,int p){
		u=++tot;if(l==r)return ;int mid=l+r>>1;
		(p<=mid)?ins(lc[u],l,mid,p):ins(rc[u],mid+1,r,p);
	}
	inline void merge(int &u,int v){
		if(!u||!v){u|=v;return ;}u=_copy(u);
		merge(lc[u],lc[v]);
		merge(rc[u],rc[v]);
	}
	inline bool query(int u,int l,int r,int ql,int qr){
		if(!u||qr<l||r<ql)return false;
		if(ql<=l&&r<=qr)return true;int mid=l+r>>1;
		return query(lc[u],l,mid,ql,qr)||query(rc[u],mid+1,r,ql,qr);
	}
}

namespace SAM{
	cs int N=::N<<1|1;
	int fa[N],son[N][26],len[N],now;
	inline void init(){for(int re i=1;i<=n;++i)len[i]=i;now=n+1;}
	inline void push_back(int i,int c){
		int p=(i==1)?(n+1):(i-1);
		for(;p&&!son[p][c];p=fa[p])son[p][c]=i;
		if(!p)fa[i]=n+1;
		else if(len[son[p][c]]==len[p]+1)fa[i]=son[p][c];
		else {
			int nq=++now,q=son[p][c];
			memcpy(son[nq],son[q],sizeof son[q]);
			len[nq]=len[p]+1,fa[nq]=fa[q];
			fa[q]=fa[i]=nq;
			for(;p&&son[p][c]==q;p=fa[p])son[p][c]=nq;
		}
	}
	int bin[N],nd[N],rt[N];
	inline void radix_sort(){
		for(int re i=1;i<=now;++i)++bin[len[i]];
		for(int re i=1;i<=n;++i)bin[i]+=bin[i-1];
		for(int re i=1;i<=now;++i)nd[bin[len[i]]--]=i;
		for(int re i=1;i<=n;++i)SGT::ins(rt[i],1,n,i);
		for(int re i=now;i>1;--i)SGT::merge(rt[fa[nd[i]]],rt[nd[i]]);
	}
	int ps[N];
	inline void query(){
		int l=gi(),r=gi(),ok=false;tl=get_s(t+1);
		int lim=std::min(r-l,tl);ps[0]=n+1;
		for(int re i=1;i<=lim;++i){
			if(son[ps[i-1]][t[i]])ps[i]=son[ps[i-1]][t[i]];
			else lim=i-1;
		}
		for(int re i=lim;!ok&&~i;--i){
			for(int re c=i!=tl?t[i+1]+1:0;c<26;++c)
			if(SGT::query(rt[son[ps[i]][c]],1,n,l+i,r)){
				for(int re j=1;j<=i;++j)putchar(t[j]+'a');putchar(c+'a');putchar('\n');
				ok=true;break;
			}
		}
		if(!ok)puts("-1");
	}
}

signed main(){
#ifdef zxyoi
	freopen("security.in","r",stdin);
#endif
	n=get_s(s+1);SAM::init();
	for(int re i=1;i<=n;++i)SAM::push_back(i,s[i]);
	SAM::radix_sort();int q=gi();while(q--)SAM::query();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值