[十二省联考 2019] 字符串问题

题目描述

现有一个字符串 \(S\)

Tiffany 将从中划出 \(n_a\) 个子串作为 \(A\) 类串,第 \(i\) 个(\(1 \leqslant i \leqslant n_a\))为 \(A_i = S(la_i, ra_i)\)

类似地,Yazid 将划出 \(n_b\) 个子串作为 \(B\) 类串,第 \(i\) 个(\(1 \leqslant i \leqslant n_b\))为 \(B_i = S(lb_i, rb_i)\)

现额外给定 \(m\) 组支配关系,每组支配关系 \((x, y)\) 描述了第 \(x\)\(A\) 类串支配\(y\)\(B\) 类串。

求一个长度最大的目标串 \(T\),使得存在一个串 \(T\) 的分割 \(T = t_1+t_2+· · ·+t_k\)\(k \geqslant 0\))满足:

  • 分割中的每个串 \(t_i\) 均为 \(A\) 类串:即存在一个与其相等的 \(A\) 类串,不妨假设其为 \(t_i = A_{id_i}\)
  • 对于分割中所有相邻的串 \(t_i, t_{i+1}\)\(1 \leqslant i < k\)),都有存在一个\(A_{id_i}\) 支配的 \(B\) 类串,使得该 \(B\) 类串为 \(t_{i+1}\) 的前缀。

方便起见,你只需要输出这个最大的长度即可。

特别地,如果存在无限长的目标串(即对于任意一个正整数 \(n\),都存在一个满足限制的长度超过 \(n\) 的串),请输出 \(-1\)

对于所有测试点中的每一组数据,保证:\(1 \leqslant |S| \leqslant 2 \times 10^5\)\(n_a\), \(n_b \leqslant 2 \times 10^5\)\(m \leqslant 2 \times 10^5\)

考虑建图,对于一个串 \(A\),向他可以到的所有 \(A\) 串连边,跑拓扑,复杂度 \(O(n^2)\)

这是单一串的子串为题,考虑用 SAM 优化建图。对于一个 \(A\) 串连向的 \(B\) 串的所有字符,在 fail 树上的体现就是一个 \(A\) 串连向某一棵子树。可以用倍增找到某一个区间在 SAM 上的节点。

但是要处理若干个区间都对于 SAM 上同一个节点的情况,所以可以先把所有这样的区间用一个 vector 存在对应的节点上,排序后重建 fail 树就行了。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=4e5+5;
char s[N];
int l,r,na,nb,m,a[N],b[N],TME,n,al[N],ar[N],bl[N],br[N];
template<int N,int M>struct graph{
	int hd[N],e_num;
	struct edge{
		int v,nxt,w;
	}e[M];
	void add_edge(int u,int v,int w)
	{
		e[++e_num]=(edge){v,hd[u],w};
		hd[u]=e_num;
	}
	void clear()
	{
		e_num=0;
		memset(hd,0,sizeof(hd));
	}
};
struct SAM{
	int tr[N<<1][26],fil[20][N<<2],idx,lst,l[N<<2],in[N<<2],ed[N],q[N<<2];
	LL dp[N<<2];
	graph<N<<2,N<<2> f;
	vector<int>g[N<<1];
	void insert(int s)
	{
		int p=++idx,k=lst;
		lst=p;
		l[p]=l[k]+1;
		ed[l[p]]=p;
		while(k&&!tr[k][s])
			tr[k][s]=p,k=fil[0][k];
		if(!k)
			fil[0][p]=1;
		else
		{
			int q=tr[k][s];
			if(l[q]==l[k]+1)
				fil[0][p]=q;
			else
			{
				int u=++idx;
				memcpy(tr[u],tr[q],sizeof(tr[u]));
				fil[0][u]=fil[0][q],l[u]=l[k]+1;
				fil[0][q]=fil[0][p]=u;
				while(k&&tr[k][s]==q)
					tr[k][s]=u,k=fil[0][k];
			}
		}
	}
	void add_edge(int u,int v,int w)
	{
		f.add_edge(u,v,w);
		in[v]++;
	}
	void init1()
	{
		for(int i=1;i<20;i++)
			for(int j=1;j<=idx;j++)
				fil[i][j]=fil[i-1][fil[i-1][j]];
	}
	void init2()
	{
		for(int i=2;i<=idx;i++)
			add_edge(fil[0][i],i,0);
	}
	int find(int l,int r)
	{
		int k=ed[r];
		for(int i=19;~i;--i)
			if(SAM::l[fil[i][k]]>=r-l+1)
				k=fil[i][k];
		return k;
	}
	void ins(int l,int r)
	{
		int k=ed[r];
		for(int i=19;~i;--i)
			if(SAM::l[fil[i][k]]>=r-l+1)
				k=fil[i][k];
		if(SAM::l[k]!=r-l+1)
			g[k].push_back(r-l+1);
	}
	void init3() 
	{
		for(int i=1;i<=idx;i++)
		{
			sort(g[i].begin(),g[i].end());
			int lst=fil[0][i];
			for(int j=0;j<g[i].size();j++)
			{
				if(j&&g[i][j]==g[i][j-1]&&g[i][j])
					continue;
				int u=++idx;
				fil[0][u]=lst,l[u]=g[i][j];
				lst=u;
			}
			fil[0][i]=lst;
		}
		for(int i=1;i<20;i++)
			for(int j=1;j<=idx;j++)
				fil[i][j]=fil[i-1][fil[i-1][j]];
	}
	LL tuopu()
	{
		int l=1,r=0;
		for(int i=1;i<=idx;i++)
			if(!in[i])
				q[++r]=i;
		while(l<=r)
		{
			for(int i=f.hd[q[l]];i;i=f.e[i].nxt)
			{
				--in[f.e[i].v];
				if(!in[f.e[i].v])
					q[++r]=f.e[i].v;
				dp[f.e[i].v]=max(dp[f.e[i].v],dp[q[l]]+f.e[i].w);
			}
			++l;
		}
		for(int i=1;i<=idx;i++)
			if(in[i])
				return -1;
		return dp[idx+1];
	}
	void clear()
	{
		memset(tr,0,sizeof(tr));
		for(int i=1;i<=idx;i++)
			g[i].clear();
		memset(dp,0,sizeof(dp));
		f.clear();
		memset(in,0,sizeof(in));
		memset(fil,0,sizeof(fil));
		lst=idx=1;
	}
}p;
int main()
{
	//freopen("3.in","r",stdin);
	scanf("%d",&TME);
	while(TME--)
	{
		p.clear();
		scanf("%s",s+1),n=strlen(s+1);
		for(int i=n;i>=1;i--)
			p.insert(s[i]-'a');
		p.init1(); 
		scanf("%d",&na);
		for(int i=1;i<=na;i++) 
			scanf("%d%d",al+i,ar+i),p.ins(n-ar[i]+1,n-al[i]+1);
		scanf("%d",&nb);
		for(int i=1;i<=nb;i++)
			scanf("%d%d",bl+i,br+i),p.ins(n-br[i]+1,n-bl[i]+1);
		p.init3();
		p.init1();
		for(int i=1;i<=na;i++)
			a[i]=p.find(n-ar[i]+1,n-al[i]+1);
		for(int i=1;i<=nb;i++)
			b[i]=p.find(n-br[i]+1,n-bl[i]+1);
		scanf("%d",&m);
		for(int i=1,x,y;i<=m;i++)
			scanf("%d%d",&x,&y),p.add_edge(a[x],b[y],p.l[a[x]]);
		for(int i=1;i<=na;i++)
			p.add_edge(a[i],p.idx+1,p.l[a[i]]);
		p.init2();
		printf("%lld\n",p.tuopu());
	}
	return 0;
}
posted @ 2023-08-06 23:03  灰鲭鲨  阅读(12)  评论(0编辑  收藏  举报