• 【题解】CF700E Cool Slogans


    CF700E Cool Slogans

    ( ext{Solution:})

    (dp,) 思路都是对的 又死细节上了 对 SAM 的理解还是不够……(或者应该说是 (dp)

    首先考虑一下什么情况我们可以接上一个串。题目给的是出现了两次,那转化到 SAM 上,我们如何用已知信息来判别?

    首先证明一个结论,如果 (t) 可以接到当前串的后面,那必然存在一种最优方案使 (s)(t) 的后缀。

    若不是,考虑把后面多余的切掉不会劣。

    那么就可以直接扔到 parent 树上考虑了。继续考虑在这种情况下我们如何判定。

    假定我们已经求出了一个 (t) 的 endpos,

    直接扔结论:

    [exist iin [endpos-len[t]+len[s],endpos-1],iin endpos_s ]

    证明:首先其作为后缀已经出现了一次,剩下的一次需要在 (endpos) 之前,最右端也就是 (endpos-1;) 而左端至少要到起点加上其长度的位置。

    那就可以愉快 (dp) 了,设 (f[i]) 表示以 parent 树上 (i) 节点结尾的最长长度,显然如果符合条件就直接 (f[i]=f[x]+1.) 不符合呢?

    当时写的时候就默认给删了……实际上这样不对,显然舍去了一些答案呀……

    观察一下,在树上一个后缀容易在深度更深的后缀中出现两次。

    这也就意味着我们的直接舍去的做法是错的。所以我们还需要记录一下一个点的前驱状态。

    初始的时候转移状态默认为自己,如果一个节点其父亲无法与当前点匹配,那就令其前驱状态为其父亲。

    转移的时候均默认从前驱状态转移即可。复杂度是 (O(nlog n),) 因为需要线段树合并维护 endpos 来支持查询。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=5e5+10;
    const int TN=1e7+10;
    inline int Min(int x,int y){return x<y?x:y;}
    inline int Max(int x,int y){return x>y?x:y;}
    int n;
    char s[N];
    namespace SGT{
    	int ls[TN],rs[TN],node;
    	void change(int &x,const int &L,const int &R,const int &pos){
    		if(!x)x=++node;
    		if(L==R)return;
    		int mid=(L+R)>>1;
    		if(pos<=mid)change(ls[x],L,mid,pos);
    		else change(rs[x],mid+1,R,pos);
    	}
    	int merge(const int &x,const int &y){
    		if(!x||!y)return x|y;
    		int p=++node;
    		ls[p]=merge(ls[x],ls[y]);
    		rs[p]=merge(rs[x],rs[y]);
    		return p;
    	}
    	bool query(const int &x,const int &L,const int &R,const int &l,const int &r){
    		if(!x)return false;
    		if(L>=l&&R<=r)return true;
    		int mid=(L+R)>>1;
    		bool res=false;
    		if(l<=mid)res|=query(ls[x],L,mid,l,r);
    		if(mid<r)res|=query(rs[x],mid+1,R,l,r);
    		return res;
    	}
    }
    using namespace SGT;
    namespace SAM{
    	int len[N],pa[N],ch[N][26],last=1,tot=1,col[N],minr[N],f[N],rt[N];
    	int siz[N],prestate[N];
    	vector<int>G[N];
    	queue<int>q;
    	void insert(const int &c,const int &cl){
    		int p=last;int np=++tot;last=tot;siz[np]=1;
    		len[np]=len[p]+1;col[np]=cl;minr[np]=cl;
    		for(;p&&!ch[p][c];p=pa[p])ch[p][c]=np;
    		if(!p)pa[np]=1;
    		else{
    			int q=ch[p][c];
    			if(len[q]==len[p]+1)pa[np]=q;
    			else{
    				int nq=++tot;
    				len[nq]=len[p]+1;
    				pa[nq]=pa[q];pa[q]=pa[np]=nq;
    				memcpy(ch[nq],ch[q],sizeof ch[q]);
    				for(;p&&ch[p][c]==q;p=pa[p])ch[p][c]=nq;
    			}
    		}
    	}
    	void dfs(int x){
    		if(col[x])change(rt[x],1,n,col[x]);
    		for(auto v:G[x]){
    			dfs(v);
    			rt[x]=merge(rt[x],rt[v]);
    			minr[x]=Max(minr[x],minr[v]);
    			siz[x]+=siz[v];
    		}
    	}
    	void Build(){
    		for(int i=2;i<=tot;++i)G[pa[i]].push_back(i);
    		dfs(1);
    	}
    	void Dp(){
    		q.push(1);f[1]=0;
    		while(!q.empty()){
    			int x=q.front();
    			q.pop();
    			for(auto v:G[x]){
    				int endpos=minr[v];
    //				printf("%d:[%d %d]
    ",v,endpos-len[v]+len[x],endpos-1);
    				if(query(rt[prestate[x]],1,n,endpos-len[v]+len[prestate[x]],endpos-1))f[v]=Max(f[v],f[prestate[x]]+1);//,cout<<"?
    ";
    				else {
                        f[v]=Max(f[v],f[x]),prestate[v]=prestate[x];
                    }
                    q.push(v);
    			}
    		}
    	}
    }
    using namespace SAM;
    int ep[N];
    int main(){
    // 	freopen("in.txt","r",stdin);
    	scanf("%d",&n);
    	scanf("%s",s+1);
        memset(minr,-0x3f,sizeof minr);
    	for(int i=1;i<=n;++i){
    		int v=s[i]-'a';
    		ep[i]=tot+1;
    		insert(v,i);
    	}
        for(int i=1;i<=tot;++i)f[i]=1,prestate[i]=i;
    	Build();
    //	for(int i=1;i<=n;++i){
    //		printf("%d %d %d
    ",ep[i],siz[ep[i]],minr[ep[i]]);
    //	}
    //	for(int i=1;i<=tot;++i)cout<<minr[i]<<" ";
    //	cout<<endl;
    	Dp();
    //	for(int i=1;i<=tot;++i)cout<<f[i]<<" ";
    //	puts("");
    	int ans=-1;
    	for(int i=1;i<=tot;++i)ans=Max(ans,f[i]);
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    vue分页组件(二)
    ES6里let、const、var区别总结
    electron-vue项目打包踩坑记录
    node环境变量配置
    第一次把本地项目与git相连
    java项目环境搭建
    npm上发布vue插件
    HTML5日期时间输入类型注意事项(time,date)
    vue 分页组件
    vue里ref ($refs)用法
  • 原文地址:https://www.cnblogs.com/h-lka/p/15201093.html
Copyright © 2020-2023  润新知