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

    题目大意

    题解

    不难但是因为字符串太菜所以想了很久

    排行榜上跑得快的一些做法假了,不知道有没有更简单的做法


    结论:存在一种最优序列,使得Si是Si-1的后缀,证明把任意一种最优的不断删掉末尾将其顶住

    也可以同时满足开头但是不需要,这样可以写个O(n^2)KMP暴力来拍

    把SAM建出来,同一个点上的串两两之间不能放一起,维护f[i][0/1]表示到点i时最长序列的结尾点以及长度,显然按照贪心解是最优的,问题就是快速判断一个串是否在另一个串中出现>=2次

    设|A|<|B|,那么随便找B的一个right设为x,A在[x-|B|+|A|,x]中的right个数就是出现次数

    如果全部维护不好搞(也许可以线段树合并),那么把right按顺序加入,每个点在最小的right处计算

    因为是后缀关系,所以只需要在前面找到最大的right即可,线段树维护+判断

    时间复杂度O(nlogn)

    code

    #include <bits/stdc++.h>
    #define fo(a,b,c) for (a=b; a<=c; a++)
    #define fd(a,b,c) for (a=b; a>=c; a--)
    #define min(a,b) (a<b?a:b)
    #define max(a,b) (a>b?a:b)
    #define ll long long
    //#define file
    using namespace std;
    
    int tr[1600011],A[200001],a[400011][2],ls[400011],Tr[400011][26],fa[400011],bg[400011],ed[400011],Len[400011];
    int f[400011][2],sum[400011],n,I,i,j,k,l,len,len2,ans;
    bool bz[400011];
    char st[200001];
    
    void copy(int t1,int t2) {memcpy(Tr[t1],Tr[t2],sizeof(Tr[t1]));}
    void New(int t,int x) {++len;if (Tr[t][x]) copy(len,Tr[t][x]);Tr[t][x]=len,Len[len]=Len[t]+1;}
    void NEW(int x,int y) {++len2;a[len2][0]=y;a[len2][1]=ls[x];ls[x]=len2;}
    void build()
    {
    	len=1;l=1;
    	fo(i,1,n)
    	{
    		New(l,st[i]-'a'),A[i]=len;
    		j=fa[l];
    		while (j && !Tr[j][st[i]-'a']) Tr[j][st[i]-'a']=len,j=fa[j];
    		
    		if (!j) fa[len]=1;
    		else
    		if (Len[j]+1==Len[Tr[j][st[i]-'a']]) fa[len]=Tr[j][st[i]-'a'];
    		else
    		{
    			k=Tr[j][st[i]-'a'],New(j,st[i]-'a');
    			fa[len]=fa[k],fa[len-1]=len,fa[k]=len;
    			j=fa[j];
    			while (j && Tr[j][st[i]-'a']==k) Tr[j][st[i]-'a']=len,j=fa[j];
    		}
    		l=Tr[l][st[i]-'a'];
    	}
    }
    
    void dfs(int t)
    {
    	int i;
    	bg[t]=++j;
    	for (i=ls[t]; i; i=a[i][1]) dfs(a[i][0]);
    	ed[t]=j;
    }
    
    void change(int t,int l,int r,int x,int s)
    {
    	int mid=(l+r)/2;
    	tr[t]=max(tr[t],s);
    	if (l==r) return;
    	
    	if (x<=mid) change(t*2,l,mid,x,s);
    	else change(t*2+1,mid+1,r,x,s);
    }
    int find(int t,int l,int r,int x,int y)
    {
    	int mid=(l+r)/2,ans=0,s;
    	if (x<=l && r<=y) return tr[t];
    	
    	if (x<=mid) s=find(t*2,l,mid,x,y),ans=max(ans,s);
    	if (mid<y) s=find(t*2+1,mid+1,r,x,y),ans=max(ans,s);
    	return ans;
    }
    
    void dg(int t)
    {
    	int i,j,k,l;
    	if (bz[t]) return;
    	bz[t]=1;
    	
    	if (fa[t]==1) f[t][0]=t,f[t][1]=sum[t]=1;
    	else
    	{
    		dg(fa[t]);
    		j=find(1,1,len,bg[f[fa[t]][0]],ed[f[fa[t]][0]]);
    		if (I-j+f[fa[t]][1]<=Len[t]) f[t][0]=t,f[t][1]=max(I-j+f[fa[t]][1],Len[fa[t]]+1),sum[t]=sum[fa[t]]+1;
    		else f[t][0]=f[fa[t]][0],f[t][1]=f[fa[t]][1],sum[t]=sum[fa[t]];
    	}
    }
    void work()
    {
    	fo(I,1,n) dg(A[I]),change(1,1,len,bg[A[I]],I);
    	fo(i,1,len) ans=max(ans,sum[i]);
    }
    
    int main()
    {
    	#ifdef file
    	freopen("CF700E.in","r",stdin);
    	freopen("a.out","w",stdout);
    	#endif
    	
    	scanf("%d",&n);
    	scanf("%s",st+1);
    	
    	build();
    	j=0;fo(i,2,len) NEW(fa[i],i); dfs(1);
    	work();
    	printf("%d
    ",ans);
    	
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    JS判断页面是否加载完成
    简单的前端验证码
    如何让旧浏览器支持HTML5新标签
    JSON使用(4)
    JSON语法(3)
    JSON简介(2)
    JSON教程(1)
    jQuery-noConflict()
    jQuery
    jQuery
  • 原文地址:https://www.cnblogs.com/gmh77/p/13635212.html
Copyright © 2011-2022 走看看