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

题意

考虑一个\(O(n^2)\)暴力:
从每个\(B\)类串向以它为前缀的\(A\)类串连边,从每个\(A\)类串向它支配的\(B\)类串连边,每个\(A\)类串的点权为\(A\)串的长度,\(B\)类串的点权为\(0\)

之后先判断这是不是个\(DAG\),如果不是就输出\(-1\),不然就找最长链即可。

之后考虑怎么优化建图:
我们先设上面说的边是\(i->j->k\),再整理下\((i,j,k)\)这个三元组的关系:\(i\)支配\(j\)\(j\)\(k\)的前缀。

显然\(i->j\)这种边只会有\(m\)条,我们只需考虑怎么优化\(j->k\)这种边。

前缀不好处理,我们将串\(s\)翻转,设为\(t\)。这样前缀就变为后缀,即在反串\(t\)\(j\)\(k\)的后缀。

我们对\(t\)建一个\(SAM\),那么对于一个节点\(k\),满足条件的\(j\)有两种情况:1.\(k\)的祖先。2.与\(k\)在同一节点,且长度小于\(k\)的串。

对于第一类我们直接父亲向儿子连边就好了,现在考虑怎么处理第二类。

我们不妨对每个结点开一个\(vector\)存这个节点代表的所有\(A,B\)串,之后对于每个结点,我们将它\(vector\)中的串按照长度为第一关键字,是否为\(B\)串为第二关键字从小到大排序排序。

对于同一个\(vector\)中的串,从每个\(B\)串向第一个比它长的\(B\)串(设为\(S\))连边,再从这个\(B\)串向每个比\(S\)小比它长的\(A\)串连边。

于是图就建完了,我们跑拓扑排序即可。

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=8e5+10;
int T,n,n1,n2,m,cnt_edge,tot;
int pos[maxn],posa[maxn],posb[maxn],a[maxn],head[maxn],in[maxn];
int f[maxn][20];
ll ans;
ll dis[maxn];
char s[maxn];
struct edge{int to,nxt;}e[maxn];
struct Str{int op,len;}str[maxn];
vector<int>ve[maxn];
struct SAM
{
	int tot,last;
	int fa[maxn],len[maxn];
	int ch[maxn][26];
	inline void clear()
	{
		for(int i=1;i<=tot;i++)fa[i]=len[i]=0;
		for(int i=1;i<=tot;i++)
			for(int j=0;j<26;j++)
				ch[i][j]=0;
		tot=last=1;
	}
	inline void add(int c)
	{
		int now=++tot;len[now]=len[last]+1;
		int p=last;last=now;
		while(p&&!ch[p][c])ch[p][c]=now,p=fa[p];
		if(!p){fa[now]=1;return;}
		int q=ch[p][c];
		if(len[q]==len[p]+1)fa[now]=q;
		else
		{
			int nowq=++tot;len[nowq]=len[p]+1;
			memcpy(ch[nowq],ch[q],sizeof(ch[nowq]));
			fa[nowq]=fa[q];fa[q]=fa[now]=nowq;
			while(p&&ch[p][c]==q)ch[p][c]=nowq,p=fa[p];
		}
	}
}sam;
inline int read()
{
	char c=getchar();int res=0,f=1;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
	return res*f;
}
inline bool cmp(int x,int y){return (str[x].len==str[y].len)?(str[x].op>str[y].op):(str[x].len<str[y].len);}
inline void add_edge(int u,int v)
{
	e[++cnt_edge].nxt=head[u];
	head[u]=cnt_edge;
	e[cnt_edge].to=v;
	in[v]++;
}
inline void init()
{
	sam.clear();
	for(int i=1;i<=tot;i++)
	{
		ve[i].clear();
		str[i]=(Str){0,0};
		head[i]=in[i]=dis[i]=0;
	}
	ans=cnt_edge=tot=0;
}
int find(int l,int r)
{
	int now=pos[l];
	for(int i=18;~i;i--)if(sam.len[f[now][i]]>=(r-l+1))now=f[now][i];
	return now;
}
inline void topsort()
{
	queue<int>q;
	for(int i=1;i<=tot;i++)if(!in[i])q.push(i);
	while(!q.empty())
	{
		int x=q.front();q.pop();
		ans=max(ans,dis[x]+((str[x].op==1)?str[x].len:0));
		for(int i=head[x];i;i=e[i].nxt)
		{
			int y=e[i].to;in[y]--;
			dis[y]=max(dis[y],dis[x]+((str[x].op==1)?str[x].len:0));
			if(!in[y])q.push(y);
		}
	}
	for(int i=1;i<=tot;i++)if(in[i])ans=-1;
}
inline void solve()
{
	init();
	scanf("%s",s+1);n=strlen(s+1);
	for(int i=n;i;i--)sam.add(s[i]-'a'),pos[i]=sam.last;
	for(int i=1;i<=sam.tot;i++)f[i][0]=sam.fa[i];
	for(int j=1;j<=18;j++)
		for(int i=1;i<=sam.tot;i++)
			f[i][j]=f[f[i][j-1]][j-1];
	n1=read();
	tot=sam.tot;
	for(int i=1;i<=n1;i++)
	{
		int l=read(),r=read();
		int now=find(l,r);
		str[++tot]=(Str){1,r-l+1};
		ve[now].push_back(tot);
		posa[i]=tot;
	}
	n2=read();
	for(int i=1;i<=n2;i++)
	{
		int l=read(),r=read();
		int now=find(l,r);
		str[++tot]=(Str){2,r-l+1};
		ve[now].push_back(tot);
		posb[i]=tot;
	}	
	for(int i=1;i<=sam.tot;i++)sort(ve[i].begin(),ve[i].end(),cmp);
	for(int i=1;i<=sam.tot;i++)
	{
		int now=i;
		for(unsigned j=0;j<ve[i].size();j++)
		{
			add_edge(now,ve[i][j]);
			if(str[ve[i][j]].op==2)now=ve[i][j];
		}
		a[i]=now;
	}
	m=read();
	for(int i=1;i<=m;i++)
	{
		int x=read(),y=read();
		add_edge(posa[x],posb[y]);
	}
	for(int i=2;i<=sam.tot;i++)add_edge(a[sam.fa[i]],i);//注意这里是a[sam.fa[i]]。
	topsort();
	printf("%lld\n",ans);
}
int main()
{
	scanf("%d",&T);
	while(T--)solve();
	return 0;
}
posted @ 2020-01-06 20:24  nofind  阅读(157)  评论(0编辑  收藏  举报