zoukankan      html  css  js  c++  java
  • 【CF1063F】String Journey

    题目

    题目链接:https://codeforces.com/problemset/problem/1063/F
    对于一个字符串数组 (t_1),....,(t_k),若对于每一个 (t_i) 都是 (t_i-1) 的真子串的话,则称为有序串组,列如 ({ab,b}) 是,({ab,c})({a,a}) 不是。
    给定字符串 (s),构造有序串组 (t_1,...,t_k) 和任意字符串数组 (u_1,...,u_{k+1}),使 (s=u_1+t_1+u_2+t_2...+t_k+u_{k+1}),其中 (+) 为字符串的拼接。
    现在给定一个字符串,求满足条件的最大 (k)
    (nleq 5 imes 10^5)

    思路

    (f_i) 表示以 (i) 开头的后缀选出有序串组的最大数量(必须选 (i))。
    首先有几个相对明显的性质:

    1. 对于一个有序串组 (t_1,t_2,cdots t_k),任意相邻的两项的长度一定差 (1)
      显然吧,不然删掉多余的字符不会更劣。

    2. 对于 (s) 一个以 (i) 开头的后缀,如果可以选出长度为 (k) 的有序串组,那么一定可以选出长度为 (k-1) 的有序串组。
      因为我们把这 (k) 字符串的最后一个字符都删去后显然得到了一个长度为 (k-1) 的有序串组。

    3. (f_{i+1}geq f_{i}-1)
      我们可以把从 (i) 开始的最长有序串组中每一个字符串的第一个字符删去,就可以得到一个从 (i+1) 开始,长度为 (f_{i}-1) 的有序串组。

    由性质 3,我们可以得到 (f_ileq f_{i+1}+1)
    这也就意味着 (sum^{n-1}_{i=1}|f_i-f_{i+1}|)(O(n)) 级别的。假设我们一定确定了 (f_{i+1}sim f_{n}),我们可以通过从 (f_{i+1}+1) 倒序枚举到 (1),并判断 (f_i) 是否可以为当前枚举的这一个值。根据性质 2,(f_i) 越大肯定越优。
    那么现在就转化成了一个判定性问题了。
    假设枚举到 (res),考虑如何判断 (f_i) 能否为 (res)。根据性质 1,如果 (f_i) 可以为 (res),那么必然存在一个 (j),满足:

    • (jgeq i+f_i)
    • (f_jgeq f_i-1)
    • (s[i:i+f_i-2]) 是字符串 (s) 中以 (j) 开头的后缀的前缀,或 (s[i+1:i+f_i-1]) 是字符串 (s) 中以 (j) 开头的后缀的前缀。

    看到一个后缀的前缀,我们不难想到建出反串的 SAM,那么在原串中,如果 (s[l:r]) 是一个后缀 (s[k:n]) 的前缀,在反串 SAM 上 (s[l:r]) 所在等价类一定是 (s[k:n]) 所在等价类在 parent 树上的祖先。
    给 parent 树的点按照 dfs 序编号后(记 (pos_i) 表示以 (i) 开头的后缀所在等价类在 parent 树上的 dfs 序,(id[i]) 表示 SAM 上点 (i) 的 dfs 序),我们的操作转化为:

    • (pos_j) 插入一个点 ((j,f_j))
    • 询问 (left [id[k],id[k]+siz[k] ight )) 的区间内是否存在二元组 ((a,b)) 满足 (ageq i+f_i),且 (bgeq f_{i}-1)

    这是一个二维偏序。但是我们发现随着 (i) 的减小,(i+f_i) 是单调不增的,所以可以直接指针维护一下第一维,第二维用线段树即可。
    时间复杂度 (O(nlog n))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=1000010,LG=20;
    int n,m,ans,tot,f[N],pos[N],head[N],siz[N],id[N],pa[N][LG+1];
    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 SAM
    {
    	int last,tot,ch[N][26],fa[N],len[N];
    	SAM() { last=tot=1; len[0]=-1; }
    	
    	void ins(int c,int j)
    	{
    		int p=last,np=++tot; 
    		len[np]=len[p]+1; last=pos[j]=np;
    		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;
    				fa[nq]=fa[q]; len[nq]=len[p]+1;
    				memcpy(ch[nq],ch[q],sizeof(ch[nq]));
    				fa[q]=fa[np]=nq;
    				for (;p && ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
    			}
    		}
    	}
    }sam;
    
    struct SegTree
    {
    	int maxn[N*4];
    	
    	void update(int x,int l,int r,int k,int v)
    	{
    		maxn[x]=max(maxn[x],v);
    		if (l==r) return;
    		int mid=(l+r)>>1;
    		if (k<=mid) update(x*2,l,mid,k,v);
    			else update(x*2+1,mid+1,r,k,v);
    	}
    	
    	int query(int x,int l,int r,int ql,int qr)
    	{
    		if (ql<=l && qr>=r) return maxn[x];
    		int mid=(l+r)>>1,res=0;
    		if (ql<=mid) res=max(res,query(x*2,l,mid,ql,qr));
    		if (qr>mid) res=max(res,query(x*2+1,mid+1,r,ql,qr));
    		return res;
    	}
    }seg;
    
    void build()
    {
    	m=sam.tot;
    	memset(head,-1,sizeof(head));
    	for (int i=2;i<=m;i++) add(sam.fa[i],i);
    }
    
    void dfs(int x)
    {
    	pa[x][0]=sam.fa[x]; id[x]=++tot; siz[x]=1;
    	for (int i=1;i<=LG;i++)
    		pa[x][i]=pa[pa[x][i-1]][i-1];
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int v=e[i].to;
    		dfs(v); siz[x]+=siz[v];
    	}
    }
    
    int find(int x,int k)
    {
    	for (int i=LG;i>=0;i--)
    		if (sam.len[pa[x][i]]>=k) x=pa[x][i];
    	return x; 
    }
    
    bool check(int i)
    {
    	int x=find(pos[i],f[i]-1),y=find(pos[i+1],f[i]-1);
    	if (seg.query(1,1,m,id[x],id[x]+siz[x]-1)>=f[i]-1) return 1;
    	if (seg.query(1,1,m,id[y],id[y]+siz[y]-1)>=f[i]-1) return 1;
    	return 0;
    }
    
    int main()
    {
    	scanf("%d%s",&n,s+1);
    	for (int i=n;i>=1;i--)
    		sam.ins(s[i]-'a',i);
    	build(); tot=0; dfs(1);
    	for (int i=n;i>=1;i--)
    	{
    		f[i]=f[i+1]+1;
    		for (;!check(i);f[i]--)
    			seg.update(1,1,m,id[pos[i+f[i]-1]],f[i+f[i]-1]);
    		ans=max(ans,f[i]);
    	}
    	cout<<ans;
    	return 0;
    }
    
  • 相关阅读:
    python re模块
    python
    python
    Django学习手册
    Django学习手册
    前端
    前端
    Django学习手册
    前端
    Database学习
  • 原文地址:https://www.cnblogs.com/stoorz/p/14884951.html
Copyright © 2011-2022 走看看