zoukankan      html  css  js  c++  java
  • 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
    ",ans);
    }
    int main()
    {
    	scanf("%d",&T);
    	while(T--)solve();
    	return 0;
    }
    
  • 相关阅读:
    Jquery清除style样式
    合并单元格式
    SQL根据下标,返回split分割后字符串
    js功能比较全面的yyyyMMdd格式的日期验证正则
    DataTable to Json
    List<T>下的Find,FindAll等条件过滤函数的使用方法
    获取iframe内部DOM对象
    PowerDesigner取消Name与Code同步
    再次回归
    最近遇到一个比较有意思的题目
  • 原文地址:https://www.cnblogs.com/nofind/p/12158344.html
Copyright © 2011-2022 走看看