zoukankan      html  css  js  c++  java
  • 【[CTSC2012]熟悉的文章】

    题目

    好题啊

    (SAM)+单调队列优化(dp)

    首先这个(L)满足单调性真是非常显然我们可以直接二分

    二分之后套一个(dp)就好了

    (dp[i])表示到达(i)位置熟悉的文章的最大长度

    有一个非常显然的(dp)方程

    [dp_i=max{dp_j+i-j} (i-j>=mid) ]

    同时([j+1,i])这个子串也得是模式串里的一个子串

    对于上面那个(dp)方程,我们把(i)提出来,用单调队列维护一下(dp_j-j)的最大值就好了

    下面这个限制条件的话,我们可以处理出以(i)这个位置的为结尾的和所有的模式串的最长公共子串的长度(mx[i])

    这个处理的话我们直接在(SAM)上过一遍就好了

    之后我们就有了第二个限制

    [j>=i-mx[i] ]

    经过分析我们可以发现(i)每次稳定加(1),而(mx[i])每次最多加(1),所以(i-mx[i])是单调不降的的

    于是我们还是可以用单调队列来维护

    复杂度(O(Tnlogn))

    代码

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define maxn 2000010
    #define re register
    #define LL long long
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    char S[maxn];
    int fa[maxn<<1],len[maxn<<1],son[maxn<<1][3];
    int mx[maxn];
    int q[maxn],dp[maxn];
    int T,m,n,cnt=1,lst=1;
    inline void ins(int c)
    {
    	int f=lst,p=++cnt; lst=p;
    	len[p]=len[f]+1;
    	while(f&&!son[f][c]) son[f][c]=p,f=fa[f];
    	if(!f) {fa[p]=1;return;}
    	int x=son[f][c];
    	if(len[f]+1==len[x]) {fa[p]=x;return;}
    	int y=++cnt;
    	len[y]=len[f]+1,fa[y]=fa[x],fa[x]=fa[p]=y;
    	for(re int i=0;i<3;i++) son[y][i]=son[x][i];
    	while(f&&son[f][c]==x) son[f][c]=y,f=fa[f];
    }
    inline void query()
    {
    	int now=1,L=0;
    	for(re int i=1;i<=n;i++)
    	{
    		if(son[now][S[i]-'0']) {mx[i]=++L;now=son[now][S[i]-'0'];continue;}
    		while(now&&!son[now][S[i]-'0']) now=fa[now];
    		if(!now) {mx[i]=L=0;now=1;continue;}
    		L=len[now]+1,mx[i]=L;now=son[now][S[i]-'0'];
    	}
    }
    inline int check(int mid)
    {
    	int h=1,t=0;
    	for(re int i=0;i<=n;i++) q[i]=dp[i]=0;
    	for(re int i=mid;i<=n;i++)
    	{
    		while(h<=t&&dp[i-mid]-i+mid>dp[q[t]]-q[t]) t--;
    		q[++t]=i-mid;
    		while(h<=t&&i-mx[i]>q[h]) h++;
    		if(h<=t) dp[i]=i+dp[q[h]]-q[h];
    		dp[i]=max(dp[i],dp[i-1]);
    	}
    	if(dp[n]*10>=n*9) return 1;
    	return 0;
    }
    int main()
    {
    	scanf("%d%d",&T,&m);
    	for(re int i=1;i<=m;i++)
    	{
    		scanf("%s",S+1); int L=strlen(S+1);
    		for(re int j=1;j<=L;j++) ins(S[j]-'0');
    		ins(2);
    	}
    	while(T--)
    	{
    		scanf("%s",S+1),n=strlen(S+1);
    		query();
    		int l=1,r=n,ans=0;
    		while(l<=r)
    		{
    			int mid=l+r>>1;
                if(check(mid)) ans=mid,l=mid+1;else r=mid-1;
    		}
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    c# 时间操作
    JAVA file文件操作
    HttpServletRequest 转换成MultipartHttpServletRequest
    【日常笔记】java spring 注解读取文件
    【日常笔记】mybatis 处理 in 语句的使用
    购物车小程序
    Python中的r+和a+
    markdown基本语法
    markdown箭头的处理
    markdown中如何插入公式
  • 原文地址:https://www.cnblogs.com/asuldb/p/10224783.html
Copyright © 2011-2022 走看看