zoukankan      html  css  js  c++  java
  • 【CF700E】Cool Slogans

    题目

    题目链接:https://codeforces.com/problemset/problem/700/E
    给定一个字符串 (S),要求构造字符串序列 (s_1,s_2,ldots,s_k),满足任意 (s_i) 都是 (S) 的子串,且任意 (iin[2,n]),都有 (s_{i-1})(s_i) 中出现了至少 (2) 次(可以有重叠部分,只要起始、结尾位置不同即可)。
    求可能的最大的 (k) 的值(即序列的最大可能长度)。
    (nleq 2 imes 10^5)

    思路

    首先有一个比较显然的结论,最终序列任意 (s_i,s{i+1}) 都应该满足 (s_i)(s_{i+1}) 的后缀。因为如果不是,那么删掉 (s_{i+1}) 后面若干个字符直到 (s_i) 是其后缀显然不劣。
    那么对于 parent 树上的两个节点 (x,y),假设 (x)(y) 的祖先,那么如果 (x) 所表示的等价类中长度最长的串在 (y) 长度最长的串中出现了至少两次,那么就可以从 (x) 转移到 (y)
    由于 (x) 中任意一个字符串必然是 (y) 的后缀,所以对于 (y)(mathrm{endpos}) 集合中任意一个元素 (mathrm{pos}_y),如果存在一个 (mathrm{pos}_x) 满足 (mathrm{pos}_xin[mathrm{pos}_y-mathrm{len}_y+mathrm{len}_x,mathrm{pos}_y)),那么 (x) 就至少在 (y) 的任意串中出现了至少两次。
    所以我们先把 SAM 建好,用可持久化线段树合并求出每一个节点的 (mathrm{endpos}),然后在 parent 树上 dp 即可。
    注意任意节点不一定只能从其父亲转移而来,所以需要记录一个 (g_x) 表示节点 (x) 是被哪一个节点转移到的。
    时间复杂度 (O(nlog n))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=400010,LG=20;
    int n,tot,ans,head[N],rt[N],a[N],b[N],f[N],g[N];
    char s[N];
    
    struct edge
    {
    	int next,to;
    }e[N];
    
    void add(int from,int to)
    {
    	e[++tot]=(edge){head[from],to};
    	head[from]=tot;
    }
    
    struct SegTree
    {
    	int tot,lc[N*LG*4],rc[N*LG*4];
    	bool flag[N*LG*4];
    	
    	int ins(int l,int r,int k)
    	{
    		int x=++tot;
    		flag[x]=1;
    		if (l==r) return x;
    		int mid=(l+r)>>1;
    		if (k<=mid) lc[x]=ins(l,mid,k);
    		if (k>mid) rc[x]=ins(mid+1,r,k);
    		return x;
    	}
    	
    	int merge(int x,int y)
    	{
    		if (!x || !y) return x|y;
    		int p=++tot;
    		flag[p]=flag[y]|flag[x];
    		lc[p]=merge(lc[x],lc[y]);
    		rc[p]=merge(rc[x],rc[y]);
    		return p;
    	}
    	
    	int query(int x,int l,int r,int ql,int qr)
    	{
    		if (ql<=l && qr>=r) return flag[x];
    		int mid=(l+r)>>1,s=0;
    		if (ql<=mid) s|=query(lc[x],l,mid,ql,qr);
    		if (qr>mid) s|=query(rc[x],mid+1,r,ql,qr);
    		return s;
    	}
    }seg;
    
    struct SAM
    {
    	int tot,last,len[N],pos[N],fa[N],ch[N][26];
    	SAM() { tot=last=1; }
    	
    	void ins(int c,int id)
    	{
    		int p=last,np=++tot;
    		last=np; len[np]=len[p]+1; pos[np]=id;
    		for (;p && !ch[p][c];p=fa[p]) ch[p][c]=np;
    		if (!p) fa[np]=1;
    		else
    		{
    			int q=ch[p][c];
    			if (len[q]==len[p]+1) fa[np]=q;
    			else
    			{
    				int nq=++tot;
    				len[nq]=len[p]+1; fa[nq]=fa[q]; pos[nq]=pos[q];
    				for (int i=0;i<26;i++) ch[nq][i]=ch[q][i];
    				fa[np]=fa[q]=nq;
    				for (;p && ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
    			}
    		}
    	}
    	
    	void buildseg()
    	{
    		for (int i=2;i<=tot;i++)
    			rt[i]=seg.ins(1,n,pos[i]);
    		for (int i=1;i<=tot;i++) b[len[i]]++;
    		for (int i=1;i<=tot;i++) b[i]+=b[i-1];
    		for (int i=1;i<=tot;i++) a[b[len[i]]--]=i;
    		for (int i=tot;i>=1;i--)
    		{
    			int j=a[i];
    			if (fa[j]!=1)
    				rt[fa[j]]=seg.merge(rt[fa[j]],rt[j]);
    			add(fa[j],j);
    		}
    	}
    	
    	void dfs(int x)
    	{
    		for (int i=head[x];~i;i=e[i].next)
    		{
    			int v=e[i].to;
    			if (x!=1 && seg.query(rt[g[x]],1,n,pos[v]-len[v]+len[g[x]],pos[v]-1))
    				f[v]=f[g[x]]+1,g[v]=v;
    			else if (x!=1)
    				f[v]=f[g[x]],g[v]=g[x];
    			else
    				f[v]=1,g[v]=v;
    			ans=max(ans,f[v]);
    			dfs(v);
    		}
    	}
    }sam;
    
    int main()
    {
    	memset(head,-1,sizeof(head));
    	scanf("%d%s",&n,s+1);
    	for (int i=1;i<=n;i++)
    		sam.ins(s[i]-'a',i);
    	sam.buildseg();
    	sam.dfs(1);
    	printf("%d",ans);
    	return 0;
    }
    
  • 相关阅读:
    总结7.13 tp5模板布局
    总结7.13 tp5图像处理
    Flask数据库
    java学习day72-JT项目10(Nginx服务器/tomcat部署/数据库高可用)
    java学习day71-Linux学习(基本指令)
    java学习day71-JT项目09(Linux/JDK/Mariadb/tomcat部署)
    java学习day70-JT项目08(图片回显/Nginx)
    java学习day69-JT项目07-(商品/详情一对一操作//文件上传)
    java学习day68-JT项目06(商品curd)
    java学习day67-JT项目05(商品分类树结构显示)
  • 原文地址:https://www.cnblogs.com/stoorz/p/14272094.html
Copyright © 2011-2022 走看看