zoukankan      html  css  js  c++  java
  • CF444D DZY Loves Strings 根号分治

    题意:

    戳这里

    分析:

    巨佬说这题是根号分治裸题,我还是太菜了

    • 暴力

    对于每一个询问,扫一遍原序列,求出 (a) 出现每一个位置,然后对于 (b) 一遍枚举出现的位置,一边双指针找出离 (b) 最近的 (a) 出现的位置,复杂度 (O(qn))

    • 正解

    我们发现,对于一个长为 (n) 的字符串,里面互不相同的长度小于 4 的子串只有 (4n) 个,其中出现次数大于 (sqrt{4n}) 的串不超过 (sqrt{4n})

    1. (a,b) 出现次数都小于 (sqrt{4n})

    我们直接按照原来的双指针枚举就行了,由于两个串出现的次数都不超过 (sqrt{4n}) ,所以此时的复杂度是 (sqrt{4n})

    1. (a,b) 至少有一个出现次数大于 (sqrt{4n})

    预处理它和每一个串的最小答案,由于这样的串不超过 (sqrt{4n}) 个,所以总的复杂度不超过 (O(sqrt{4n}))

    代码:

    #include<bits/stdc++.h>
    #define pii pair<int,int>
    #define mk(x,y) make_pair(x,y)
    #define lc rt<<1
    #define rc rt<<1|1
    #define pb push_back
    #define fir first
    #define sec second
    
    using namespace std;
    
    namespace zzc
    {
    	inline int read()
    	{
    		int x=0,f=1;char ch=getchar();
    		while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    		while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    		return x*f;
    	}
    	
    	const int maxq = 5e5+5;
    	const int maxn = 1e5+5;
    	const int maxm = 5e5+5;
    	const int blo = 400;
    	char s[maxn],a[10],b[10];
    	int mp[maxq],f[blo][maxn],id[maxn][5],len[maxn],big[maxn];
    	int n,idx,cnt,qt;
    	vector<int> pos[maxn];
    	
    	int get(char *s,int l)
    	{
    		int res=0;
    		for(int i=0;i<l;i++) res=res*26+(s[i]-96);
    		return res;
    	}
    	
    	void work()
    	{
    		scanf("%s",s+1);qt=read();n=strlen(s+1);
    		for(int l=1;l<=4;l++)
    		{
    			for(int i=1,j=n-l+1;i<=j;i++)
    			{
    				int tmp=get(s+i,l);
    				if(!mp[tmp]) mp[tmp]=++idx,len[idx]=l;
    				pos[id[i][l]=mp[tmp]].pb(i);
    			}
    		}
    		for(int i=1;i<=idx;i++)if(pos[i].size()>blo)
    		{
    			big[i]=++cnt;
    			int sz=pos[i].size(),tmp=len[i];
    			memset(f[cnt],0x3f,sizeof(f[cnt]));
    			for(int j=1;j<sz;j++)
    			{
    				int lef=pos[i][j-1];
    				int rig=pos[i][j];
    				for(int k=lef;k<rig;k++) for(int l=1;l<=4;l++) f[cnt][id[k][l]]=min(f[cnt][id[k][l]],min(max(k+l-lef,tmp),max(rig+tmp-k,l)));
    			}
    			for(int k=pos[i][sz-1],lef=pos[i][sz-1];k<=n;k++) for(int l=1;l<=4;l++) f[cnt][id[k][l]]=min(f[cnt][id[k][l]],max(k+l-lef,tmp));
    			for(int k=1,rig=pos[i][0];k<rig;k++) for(int l=1;l<=4;l++) f[cnt][id[k][l]]=min(f[cnt][id[k][l]],max(rig+tmp-k,l));
    		}
    		while(qt--)
    		{
    			scanf("%s",a);scanf("%s",b);
    			int x=mp[get(a,strlen(a))],y=mp[get(b,strlen(b))];
    			if(!x||!y) printf("-1
    ");
    			else if(big[x]) printf("%d
    ",f[big[x]][y]);
    			else if(big[y]) printf("%d
    ",f[big[y]][x]);
    			else
    			{
    				int sz1=pos[x].size()-1,sz2=pos[y].size()-1,lx=len[x],ly=len[y],ans=0x3f3f3f3f,now=0;
    				for(int i=0;i<=sz1;i++)
    				{
    					while(now<sz2&&pos[y][now]<pos[x][i]) now++;
    					if(pos[y][now]>pos[x][i])
    					{
    						ans=min(ans,pos[y][now]+ly-pos[x][i]);
    						if(now) ans=min(ans,pos[x][i]+lx-pos[y][now-1]);
    					}
    					else ans=min(ans,pos[x][i]+lx-pos[y][now]);
    				}
    				printf("%d
    ",max(ans,max(lx,ly)));
    			}
    		}
    	}
    
    }
    
    int main()
    {
    	zzc::work();
    	return 0;
    }
    
  • 相关阅读:
    C#中的const和readonly之间的不同(转)
    文字在状态栏上从右往左显示,而且是循环的
    文字在状态栏上从左往右一个一个地显示
    猛然发现,已经第100篇随笔了
    怎样使按钮响应回车键
    编程之我见(二 类库)初露牛角
    编程之我见(一 语言)小试牛刀
    开始→运行→输入的命令集锦(转)收藏
    显示走动的数字时间和显示星期,年,月,日
    在两个页面之间互相写其控件内的值
  • 原文地址:https://www.cnblogs.com/youth518/p/14070744.html
Copyright © 2011-2022 走看看