1. 程式人生 > 實用技巧 >CF444D DZY Loves Strings 根號分治

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\n");
			else if(big[x]) printf("%d\n",f[big[x]][y]);
			else if(big[y]) printf("%d\n",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\n",max(ans,max(lx,ly)));
			}
		}
	}

}

int main()
{
	zzc::work();
	return 0;
}