CF1037H Security 后缀自动机 + right集合线段树合并 + 贪心

后缀自动机 + 线段树合并的裸题.

我这种大菜逼都秒切的题目,一定是送分题.

#include<bits/stdc++.h>
#define setIO(s) freopen(s".in","r",stdin)
#define maxn 220000
using namespace std;
int n;
int rt[maxn];
namespace tr
{
	#define mid ((l+r)>>1)
	#define lson t[x].l
	#define rson t[x].r
	int cnt;
	int newnode() { return ++cnt; }
	struct Node{ int l,r,sumv; }t[maxn * 50];
	int merge(int u,int v)
	{
		if(!u||!v) return u+v;
		int x=newnode();
		t[x].sumv=t[u].sumv+t[v].sumv;
		lson=merge(t[u].l,t[v].l);
		rson=merge(t[u].r,t[v].r);
		return x;
	}
	void update(int &x,int l,int r,int k,int delta)
	{
		if(!x)x=newnode();
		t[x].sumv+=delta;
		if(l==r) return;
		if(k<=mid) update(lson, l, mid, k, delta);
		else update(rson, mid + 1, r, k, delta);
	}
	int query(int x,int l,int r,int L,int R)
	{
		if(!x || L>R)return 0;
		if(l>=L&&r<=R) return t[x].sumv;
		int tmp=0;
		if(L<=mid) tmp+=query(lson,l,mid,L,R);
		if(R>mid) tmp+=query(rson, mid+1,r,L,R);
		return tmp;
	}
	#undef lson
	#undef rson
};
namespace SAM
{
	int tot,last;
	int len[maxn], ch[maxn][30], f[maxn], rk[maxn], C[maxn];
	void init() { tot = last = 1; }
	void extend(int c)
	{
		int np=++tot,p=last;
		last=np, len[np]=len[p]+1;
		while(p&&!ch[p][c]) ch[p][c]=np,p=f[p];
		if(!p) f[np]=1;
		else
		{
			int q=ch[p][c];
			if(len[q]==len[p]+1) f[np]=q;
			else
			{
				int nq=++tot;
				len[nq]=len[p]+1;
				memcpy(ch[nq],ch[q],sizeof(ch[q]));
				f[nq]=f[q],f[np]=f[q]=nq;
				while(p&&ch[p][c]==q) ch[p][c]=nq, p=f[p];
			}
		}
		tr::update(rt[np], 1, n, len[np], 1);
	}
	void prepare()
	{
		int i,j;
		for(i=1;i<=tot;++i) ++C[len[i]];
		for(i=1;i<=tot;++i) C[i]+=C[i-1];
		for(i=1;i<=tot;++i) rk[C[len[i]]--]=i;
		for(i=tot;i>=1;--i)
		{
			j=rk[i];
			rt[f[j]]=tr::merge(rt[f[j]], rt[j]);
		}
	}
};
char str[maxn], T[maxn], stac[maxn];
int cur[maxn];
int main()
{
	int i,j,Q;
	// setIO("input");
	scanf("%s",str+1);
	n=strlen(str+1);
	SAM::init();
	for(i=1;i<=n;++i)
	{
		SAM::extend(str[i]-‘a‘);
	}
	SAM::prepare();
	scanf("%d",&Q);
	while(Q--)
	{
		int l,r,_len,trace=0,top=0,L;
		scanf("%d%d%s",&l,&r,T+1);
		L=l;
		_len=strlen(T+1);
		cur[0]=1;
		for(i=1;i<=min(_len,n-1);++i)
		{
			int c=T[i]-‘a‘;
			if(SAM::ch[cur[i-1]][c] && tr::query(rt[SAM::ch[cur[i-1]][c]], 1, n, l, r))
			{
				cur[i]=SAM::ch[cur[i-1]][c];
				++l,   trace=i, stac[++top]=T[i];
			}
			else break;
		}
		T[0]=‘a‘;
		int flag=0;
		for(i=min(n-1,trace);i>=0;--i)
		{
			int c= i + 1 > _len ? -1 : T[i+1] - ‘a‘;
		//	if(flag) break;
			for(j=c+1;j<27;++j)
			{
				if(SAM::ch[cur[i]][j] && tr::query(rt[SAM::ch[cur[i]][j]] , 1, n, l, r))
				{
					flag=1;
					stac[++top]=‘a‘+j;
					break;
				}
			}
			if(flag) break;
			--l;
			--top;
		}
		if(!flag) printf("-1\n");
		else
		{
			for(i=1;i<=top;++i) printf("%c",stac[i]);
			printf("\n");
		}
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/guangheli/p/11102223.html

时间: 2024-10-07 18:28:48

CF1037H Security 后缀自动机 + right集合线段树合并 + 贪心的相关文章

CF700E Cool Slogans 后缀自动机 + right集合线段树合并 + 树形DP

又是一道 SAM 神题. Code: #include<bits/stdc++.h> #define maxn 400002 #define setIO(s) freopen(s".in","r",stdin) using namespace std; namespace tr { #define lson t[x].l #define rson t[x].r #define mid ((l+r)>>1) int cnt; struct No

【codeforces666E】Forensic Examination 广义后缀自动机+树上倍增+线段树合并

题目描述 给出 $S$ 串和 $m$ 个 $T_i$ 串,$q$ 次询问,每次询问给出 $l$ .$r$ .$x$ .$y$ ,求 $S_{x...y}$ 在 $T_l,T_{l+1},...,T_r$ 中的哪一个里出现次数最多,输出出现次数最多的串编号(如果有多个则输出编号最小的)以及相应出现次数. $|S|,q\le 5\times 10^5$ ,$\sum\limits_{i=1}^m|T_i|\le 5\times 10^4$ . 题解 广义后缀自动机+树上倍增+线段树合并 对 $S$

[BJWC2018]Border 的四种求法(后缀自动机+链分治+线段树合并)

题目描述 给一个小写字母字符串 S ,q 次询问每次给出 l,r ,求 s[l..r] 的 Border . Border: 对于给定的串 s ,最大的 i 使得 s[1..i] = s[|s|-i+1..|s|], |s| 为 s 的长度. 题解 这题的描述很短,给人一种很可做的假象. 暴力1:每次对区间lr做一次KMP,求出border数组,复杂度nq. 暴力2:构建后缀自动机,用线段树合并维护出right集合考虑到两个串的最长后缀为他们在parent树上的LCA的len,所以我们可以在pa

CF666E Forensic Examination(后缀自动机+线段树合并)

给你一个串S以及一个字符串数组T[1..m],q次询问,每次问S的子串S[pl..pr]在T[l..r]中的哪个串里的出现次数最多,并输出出现次数. 如有多解输出最靠前的那一个. 我们首先对m个字符串数组建出后缀自动机,然后我们可以通过跳trans边找到S前i个字符代表的前缀的最长后缀.我们要找的是S[pl..pr]并不是以pr结束最长的后缀,但我们可以确定S[pl..pr]一定是当前点的祖先所以当我们跳到pr代表的点时我们倍增往上跳知道找到一个点的长度刚好大于等于pr-pl+1,这个点就是询问

HDU - 6704 K-th occurrence (后缀数组+主席树/后缀自动机+线段树合并+倍增)

题意:给你一个长度为n的字符串和m组询问,每组询问给出l,r,k,求s[l,r]的第k次出现的左端点. 解法一: 求出后缀数组,按照排名建主席树,对于每组询问二分或倍增找出主席树上所对应的的左右端点,求第k大的下标即可. 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=1e5+10,mod=998244353; 5 char buf[N]; 6 int s[N],sa[

CF700E:Cool Slogans(后缀自动机,线段树合并)

Description 给你一个字符串,如果一个串包含两个不重叠的相同子串,那么这个串的价值就是子串的价值+1.问你给定字符串的最大价值子串的价值. Input 第一行读入字符串长度$n$,第二行是字符串. Output 一行答案. Sample Input1 3abc Sample Output1 1 Sample Input2 5ddddd Sample Output2 5 Sample Input3 11abracadabra Sample Output3 3 Solution 首先把后缀

bzoj 3413: 匹配 后缀自动机+线段树合并

并不是很难啊,把细节想好了再写就很轻松了~ code: #include <bits/stdc++.h> #define N 200003 #define LL long long #define setIO(s) freopen(s".in","r",stdin) ,freopen(s".out","w",stdout) using namespace std; struct SAM { int tot,last

CF666E Forensic Examination(广义后缀自动机+线段树合并)

Luogu 给你一个串 $ S $ 以及一个字符串数组 $ T_1 ~ T_m $ , $ q $ 次询问,每次问 $ S $ 的子串S[p_l,p_r]在 $ T_l ~ T_r $ 中的哪个串里的出现次数最多,并输出出现次数. 如有多解输出最靠前的那一个. 题解时间 SAM的毒瘤题,无论是倍增来满足长度限制,线段树合并来求区间询问,应有尽有... 对于 $ T $ 串建广义SAM,之后考虑如何使得 $ S $ 在SAM上匹配时求出 $ S $ 在每个 $ T $ 的出现次数. 很明显用线段树

CF666E Forensic Examination [后缀自动机,线段树合并]

题意: 给出一个串 \(S\),再给出 \(n\) 个串 \(T_i\), \(q\) 次询问 \(S[pl,pr]\) 在 $ T_{[l,r]}$哪个串出现次数最多. solution: 不难想到我们找 \(S[pl,pr]\) 是可以记录 \(ed_{pr}\) 然后倍增上去找到这个区间所对应的 SAM 节点. 我们把 \(T_i\) 插入 SAM 里,并且对应节点搞上 \(i\),然后合并就好了qwq. SAM 某个子树部分都是包含他自己的串,所以线段树合并一下就变成了子树数颜色以及找到