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

    XVI.CF700E Cool Slogans

    这题有SA和SAM两种做法,但事实证明,本题的SAM做法无论在思维难度还是在代码难度上,都爆踩SA做法。

    首先,SA做法可以参见本人的题解

    然后,SAM做法见下。

    首先,我们一定可以将每个串砍掉一部分,使得我们所需串中,前一个串必是后一个串的后缀。具体而言,因为前一个串必定在后一个串中出现至少两次,于是我们就砍掉后一个串的后缀,使得前一个串的最后一次出现的结尾处即为后一个串的结尾。这样,被砍掉后缀的后一个串,仍然至少在后后一个串中出现两次,则此方案合法。

    于是我们考虑在SAM上从上往下DP。设 \(f_i\) 表示节点 \(i\) 所对应的 \(\text{endpos}\) 集合中最长串所能套娃的层数。

    设我们当前想从 \(f_x\) 转移到其某个儿子 \(f_y\)。明显,\(f_y\) 有两种可能,一是直接就等于 \(f_x\),另一种是等于 \(f_x+1\),此时是在 \(f_x\) 外面套了一层。

    我们考虑找出 \(\text{endpos}(y)\) 中任意一个元素(因为其代表的串都是相同的,所以任选一个即可),设作 \(pos_y\)。则,在 \(pos_y\) 处,\(x\) 所对应的串已经出现过一次了,只需在剩下的位置中再出现一次即可。因为其必须完整地在 \(y\) 中出现,又不与后缀的那一次重合,所以其必须在区间 \(\big[pos_y-\text{len}(y)+\text{len}(x),pos_y-1\Big]\) 中出现。换言之,\(\text{endpos}(x)\) 中,至少要有一个元素在上述区间内。

    考虑用线段树合并维护 \(\text{endpos}\) 集合即可。这也是某些题中的经典套路。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int n,cnt=1,rt[400100],f[400100],las[400100],pos[400100],res;
    char s[200100];
    struct Suffix_Automaton{int ch[26],len,fa;}t[400100];
    int Add(int x,int c){
    	int xx=++cnt;t[xx].len=t[x].len+1;
    	for(;x&&!t[x].ch[c];x=t[x].fa)t[x].ch[c]=xx;
    	if(!x){t[xx].fa=1;return xx;}
    	int y=t[x].ch[c];
    	if(t[y].len==t[x].len+1){t[xx].fa=y;return xx;}
    	int yy=++cnt;t[yy]=t[y],t[yy].len=t[x].len+1;
    	t[xx].fa=t[y].fa=yy;
    	for(;x&&t[x].ch[c]==y;x=t[x].fa)t[x].ch[c]=yy;
    	return xx;
    }
    
    #define mid ((l+r)>>1)
    int tot;
    struct SegmentTree{
    	int lson,rson;
    }seg[20010000];
    int merge(int x,int y){
    	if(!x||!y)return x+y;
    	int z=++tot;
    	seg[z].lson=merge(seg[x].lson,seg[y].lson);
    	seg[z].rson=merge(seg[x].rson,seg[y].rson);
    	return z;
    }
    void modify(int &x,int l,int r,int P){
    	if(l>P||r<P)return;
    	if(!x)x=++tot;
    	if(l!=r)modify(seg[x].lson,l,mid,P),modify(seg[x].rson,mid+1,r,P);
    }
    bool query(int x,int l,int r,int L,int R){
    	if(l>R||r<L||!x)return false;
    	if(L<=l&&r<=R)return true;
    	return query(seg[x].lson,l,mid,L,R)|query(seg[x].rson,mid+1,r,L,R);
    }
    
    vector<int>v[400100];
    void dfs1(int x){for(auto y:v[x])dfs1(y),rt[x]=merge(rt[x],rt[y]),pos[x]=pos[y];}
    void dfs2(int x){
    	res=max(res,f[x]);
    	for(auto y:v[x]){
    		if(query(rt[las[x]],0,n-1,pos[y]-t[y].len+t[las[x]].len,pos[y]-1))f[y]=f[x]+1,las[y]=y;
    		else f[y]=f[x],las[y]=las[x];
    		dfs2(y);
    	}
    }
    int main(){
    	scanf("%d%s",&n,s);
    	for(int i=0,las=1;i<n;i++)las=Add(las,s[i]-'a'),modify(rt[las],0,n-1,i),pos[las]=i;
    	for(int i=2;i<=cnt;i++)v[t[i].fa].push_back(i);
    	dfs1(1);
    	for(auto y:v[1])f[y]=1,las[y]=y,dfs2(y);
    	printf("%d\n",res);
    	return 0;
    }
    

  • 相关阅读:
    verilog之计数器0~9999——数码管显示
    C语言的谜题
    modelsim SE6.2b的常用问题
    [转载]MDK常见报错(编译arm)
    Nios II之LED实验(SDRAM+EPCS4配置)————基于Altera的DE0开发板
    poj2210
    poj1161
    poj1555
    poj2337
    poj1107
  • 原文地址:https://www.cnblogs.com/Troverld/p/14605738.html
Copyright © 2011-2022 走看看