• Codeforces 700E. Cool Slogans 字符串,SAM,线段树合并,动态规划


    原文链接https://www.cnblogs.com/zhouzhendong/p/CF700E.html

    题解

    首先建个SAM。

    一个结论:对于parent树上任意一个点x,以及它所代表的子树内任意一个点y,设节点y代表的最长串为S,设节点x代表的串为T1,T2,T3,...,设 F(S,T) 表示串T在S中的出现次数,则 F(S,T1) = F(S,T2) = F(S,T3) = ...

    证明:假设串 Ta 和 Tb 在 S 中的出现次数不同,且 |Ta|+1=|Tb| 则必然存在一个位置,使得将 Tb 放在这里的时候,它的最左端点不和 S 匹配,其他位置都匹配,这样的话,Tb 的 Right 集合至少比 Ta 多这个位置,与 “Ta,Tb 都是节点 x 代表的串” 矛盾。故原命题得证。

    第二个结论:一定存在一个最优解,使得 $forall 1<ileq k$, $S_{i-1}$ 是 $S_i$ 的 Border 。

    这个很好证,如果不是 Border ,把 $S_i$ 两边多出来的割掉一定不亏。

    于是我们可以开始规划算法了。

    设 $dp[S]$ 表示每次保证前一个串在后一个串中出现至少 2 次,从空串转移到串 $S$ 的最多转移次数。

    我们把状态用 parent 树上的节点表示,由于第一个结论,对于每一个节点,我们可以只把这个节点代表的最长串作为有效状态;转移的时候,只要看看父亲的串在当前节点的串中出现次数是否至少2次,如果不到,就直接继承父亲的结果,否则更新为当前结果; 判断出现多少次需要处理出 Right 集合,线段树合并即可。

    代码

    #include <bits/stdc++.h>
    #define clr(x) memset(x,0,sizeof (x))
    using namespace std;
    typedef long long LL;
    LL read(){
    	LL x=0,f=0;
    	char ch=getchar();
    	while (!isdigit(ch))
    		f|=ch=='-',ch=getchar();
    	while (isdigit(ch))
    		x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    	return f?-x:x;
    }
    const int N=200005*2;
    int n;
    namespace seg{
    	const int S=N*50;
    	int ls[S],rs[S],size[S],cnt,root;
    	void Init(){
    		cnt=root=0;
    		clr(ls),clr(rs),clr(size);
    	}
    	void Ins(int &rt,int L,int R,int x){
    		if (!rt)
    			rt=++cnt;
    		size[rt]++;
    		if (L==R)
    			return;
    		int mid=(L+R)>>1;
    		if (x<=mid)
    			Ins(ls[rt],L,mid,x);
    		else
    			Ins(rs[rt],mid+1,R,x);
    	}
    	int Merge(int a,int b,int L,int R){
    		if (!a||!b)
    			return a+b;
    		int rt=++cnt;
    		if (L==R)
    			size[rt]=1;
    		else {
    			int mid=(L+R)>>1;
    			ls[rt]=Merge(ls[a],ls[b],L,mid);
    			rs[rt]=Merge(rs[a],rs[b],mid+1,R);
    			size[rt]=size[ls[rt]]+size[rs[rt]];
    		}
    		return rt;
    	}
    	int Query(int rt,int L,int R,int xL,int xR){
    		if (!rt||xL>R||L>xR)
    			return 0;
    		if (xL<=L&&R<=xR)
    			return size[rt];
    		int mid=(L+R)>>1;
    		return Query(ls[rt],L,mid,xL,xR)
    			  +Query(rs[rt],mid+1,R,xL,xR);
    	}
    }
    namespace SAM{
    	int last,size,root;
    	struct Node{
    		int Next[26],fa,Max,pos;
    	}t[N];
    	int Init(){
    		clr(t);
    		return last=size=root=1;
    	}
    	void extend(int c,int ps){
    		int p=last,np=++size,q,nq;
    		t[np].Max=t[p].Max+1,t[np].pos=ps;
    		for (;p&&!t[p].Next[c];p=t[p].fa)
    			t[p].Next[c]=np;
    		if (!p)
    			t[np].fa=root;
    		else {
    			q=t[p].Next[c];
    			if (t[p].Max+1==t[q].Max)
    				t[np].fa=q;
    			else {
    				nq=++size;
    				t[nq]=t[q],t[nq].Max=t[p].Max+1,t[nq].pos=ps;
    				t[np].fa=t[q].fa=nq;
    				for (;p&&t[p].Next[c]==q;p=t[p].fa)
    					t[p].Next[c]=nq;
    			}
    		}
    		last=np;
    	}
    	int id[N],tax[N],rt[N];
    	void sort(){
    		clr(tax);
    		for (int i=1;i<=size;i++)
    			tax[t[i].Max]++;
    		for (int i=1;i<=size;i++)
    			tax[i]+=tax[i-1];
    		for (int i=1;i<=size;i++)
    			id[tax[t[i].Max]--]=i;
    	}
    	void build(){
    		sort();
    		seg::Init();
    		for (int i=size;i>1;i--)
    			seg::Ins(rt[id[i]],1,n,t[id[i]].pos);
    		for (int i=size;i>1;i--){
    			int x=id[i],f=t[x].fa;
    			rt[f]=seg::Merge(rt[f],rt[x],1,n);
    		}
    	}
    	int dp[N],nid[N];
    	int Horse_NMDP(){
    		int ans=0;
    		dp[1]=0,nid[1]=1;
    		for (int i=2;i<=size;i++){
    			int x=id[i],f=nid[t[x].fa];
    			if (f==1||seg::Query(rt[f],1,n,t[x].pos-t[x].Max+t[f].Max
    										  ,t[x].pos)>=2)
    				dp[x]=dp[f]+1,nid[x]=x;
    			else
    				dp[x]=dp[f],nid[x]=f;
    			ans=max(ans,dp[x]);
    		}
    		return ans;
    	}
    }
    using SAM::t;
    using SAM::extend;
    char s[N];
    int main(){
    	n=read();
    	scanf("%s",s+1);
    	SAM::Init();
    	for (int i=1;i<=n;i++)
    		extend(s[i]-'a',i);
    	SAM::build();
    	cout<<SAM::Horse_NMDP()<<endl;
    	return 0;
    }
    

      

  • 相关阅读:
    C# UDP实现通信的方法
    Leetcode 559. N叉树的最大深度
    101. 对称二叉树
    108. 将有序数组转换为二叉搜索树
    剑指 Offer 55
    Linux
    Linux
    Linux
    Linux
    Linux
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/CF700E.html
Copyright © 2020-2023  润新知