【后缀数组+???】BZOJ3654 图样图森破

版权声明:这是蒟蒻的BLOG,神犇转载也要吱一声哦~ https://blog.csdn.net/Dream_Lolita/article/details/85413193

【题目】
原题地址
给定一个字符串集合 S S ,每次可以向 S S 中加入一个已经存在于 S S 中的字符串或者从 S S 中选出两个字符串,将它们拼接后得到的字符串加入 S S S 100 S i 1000 |S|\leq 100,|S_i|\leq 1000

【解题思路】
第一眼看肯定先判掉单个串是回文串,然后就陷入了沉思。
分析了很久以后看出来,如果答案不是 I N F INF ,那么最长回文子串只可能出现在一个串里,或者出现在两个串拼接一次形成的串里(还有可能是三个呢)。
然后就有了一个很 naive \text{naive} 的想法:枚举两个串拼接,再枚举一个回文中心点,这样我们应该只需要用后缀数组的 O ( 1 ) LCP O(1)\text{LCP} 瞎搞就行了。这个我们当然是可以做的了,不过复杂度是 O ( n 2 L + n L log ( n L ) ) O(n^2L+nL\log (nL)) 的,十分不优美。

不过这个做法比QT的什么鬼图论好搞多了,我们直接暴力算了。

等等,还有一个问题是要看能不能长度无限,不过这个问题也很好解决吧,其实本质上来说,就是匹配不能形成一个环。
于是我们设 f i , 0 f_{i,0} 表示 i i 这个位置向后能扩展到的回文串最长是多少, f i , 1 f_{i,1} 表示向前。仍然像上面一样枚举中心点,当超过长度枚举串拼接,不同的是,我们用记忆化搜索来判断拼接后是否会产生环的转移,这样应该可以省去很多特判。

嗯,卡一卡就能过了。
然后跑得巨快,是我复杂度算错了?
顺带一提,以后写代码一定要开Wall。
update:
经提醒发现答案还可能由三个串拼在一起,但是记忆化来做可以保证正确性。

【参考代码】

#include<bits/stdc++.h>
using namespace std;

const int N=2e5+10;
int n,m,ans;
int bl[N],st[N],en[N],s[N],f[N][2];
bool vis[N][2],bo[N][2];
char ss[N];

namespace SA
{
	int len,rk[N],hi[N],fc[30],Log[N],h[22][N];
	int wa[N],wb[N],wx[N],wy[N],sa[N];
	bool cmp(int *r,int a,int b,int l){return r[a]==r[b] && r[a+l]==r[b+l];}
	void getsa(int *r,int n,int m)
	{
		int *x=wa,*y=wb,*t,i,j,p;
		for(i=0;i<m;++i) wx[i]=0;
		for(i=0;i<n;++i) wx[x[i]=r[i]]++;
		for(i=1;i<m;++i) wx[i]+=wx[i-1];
		for(i=n-1;~i;--i) sa[--wx[x[i]]]=i;
		for(j=1,p=1;p<n;j<<=1,m=p)
		{
			for(p=0,i=n-j;i<n;++i) y[p++]=i;
			for(i=0;i<n;++i) if(sa[i]>=j) y[p++]=sa[i]-j;
			for(i=0;i<m;++i) wx[i]=0;
			for(i=0;i<n;++i) wx[wy[i]=x[y[i]]]++;
			for(i=1;i<m;++i) wx[i]+=wx[i-1];
			for(i=n-1;~i;--i) sa[--wx[wy[i]]]=y[i];
			for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;++i) 
				x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
		}
	}
	void adjust(int n)
	{
		for(int i=1;i<=n;++i) sa[i]++;
		for(int i=n;i;--i) rk[i]=rk[i-1];
		sa[0]=rk[0]=hi[0]=0;
	}
	void getheight(int *r,int n)
	{
		int i,j,k=0;
		for(i=1;i<n;++i) rk[sa[i]]=i;
		for(i=0;i<n;hi[rk[i++]]=k)
			for(k?k--:0,j=sa[rk[i]-1];r[i+k]==r[j+k];++k);
		//adjust(len);
		fc[0]=1;for(int i=1;i<20;++i) fc[i]=fc[i-1]<<1;
		for(int i=2;i<n;++i) Log[i]=Log[i>>1]+1;
		for(int i=1;i<n;++i) h[0][i]=hi[i];
		//cerr<<n<<endl;
		for(int j=1;j<19;++j) for(int i=1;i+fc[j]-1<n;++i)
			h[j][i]=min(h[j-1][i],h[j-1][i+fc[j-1]]);
	}
	void buildSA(){len=m+1;getsa(s,len,30);getheight(s,len);}
	int calc(int x,int y)
	{
		int t=Log[y-x+1];
		//printf("%d %d\n",h[t][x],h[t][y-fc[t]+1]);
		return min(h[t][x],h[t][y-fc[t]+1]);
	}
	int lcp(int x,int y)
	{
		if(x==y) return N;
		//printf("lcp:%d %d %d %d %d\n",x,y,rk[x],rk[y],calc(x+1,y));
		if((x=rk[x])>(y=rk[y])) swap(x,y);
		//cerr<<x<<" "<<y<<endl;
		return calc(x+1,y);
	}
	int query(int x,int y)
	{
		//printf("%d %d %d %d %d %d\n",m,x,y,min(lcp(x,m-y-1),min(en[bl[x]]-x,y-st[bl[y]]+1)),lcp(x,m-y-1),min(en[bl[x]]-x,y-st[bl[y]])+1);
		return min(lcp(x,m-y-1),min(en[bl[x]]-x,y-st[bl[y]])+1);
	}
	void output()
	{
		for(int i=0;i<=len;++i) printf("%d ",sa[i]); puts("");
		for(int i=0;i<=len;++i) printf("%d ",rk[i]); puts("");
		for(int i=0;i<=len;++i) printf("%d ",hi[i]); puts("");
	}
}
using SA::buildSA;
using SA::query;

void putINF(){puts("Infinity");exit(0);}
int dfs(int x,int p)
{
	if(vis[x][p]) putINF();
	if(bo[x][p]) return f[x][p];
	vis[x][p]=bo[x][p]=1;
	int &res=f[x][p];
	if(!p)
	{
		for(int i=1;i<=n;++i)
		{
			int t=query(x,en[i]),a=x+t-1,b=en[i]-t+1;
			if(a<en[bl[x]] && b>st[i]) res=max(res,t*2);
			else if(a==en[bl[x]] && b==st[i]) putINF();
			else if(a==en[bl[x]]) res=max(res,t*2+dfs(b-1,1));
			else res=max(res,t*2+dfs(a+1,0));
		}
	} 
	else
	{
		for(int i=1;i<=n;++i)
		{
			//cerr<<i<<" "<<st[i]<<" "<<x;
			int t=query(st[i],x),a=x-t+1,b=st[i]+t-1;
			if(a>st[bl[x]] && b<en[i]) res=max(res,t*2);
			else if(a==st[bl[x]] && b==en[i]) putINF();
			else if(a==st[bl[x]]) res=max(res,t*2+dfs(b+1,0));
			else res=max(res,t*2+dfs(a-1,1));
		}
	}
	vis[x][p]=0; 
	return res;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("BZOJ3654.in","r",stdin);
	freopen("BZOJ3654.out","w",stdout);
#endif
	scanf("%d",&n);
	for(int i=1,l;i<=n;++i)
	{
		scanf("%s",ss);l=strlen(ss);st[i]=m;
		for(int j=0;j<l;++j) bl[m]=i,s[m++]=ss[j]-'a'+1;
		en[i]=m-1;
		//printf("%d %d\n",st[i],en[i]);
	}
	m<<=1;
	for(int i=0,j=m-1;i<j;++i,--j) s[j]=s[i],bl[j]=bl[i];
	SA::buildSA();//SA::output();
	for(int i=1;i<=n;++i) ans=max(ans,max(dfs(st[i],0),dfs(en[i],1)));
//printf("%d\n",ans);
	for(int i=1;i<=n;++i)
	{
		for(int j=st[i];j<=en[i];++j)
		{
			int t=query(j,j),a=j-t+1,b=j+t-1;
			if(a>st[i] && b<en[i]) ans=max(ans,t*2-1);
			else if(a==st[i] && b==en[i]) putINF();
			else if(a==st[i]) ans=max(ans,t*2-1+dfs(b+1,0));
			else ans=max(ans,t*2-1+dfs(a-1,1));
		}//odd
		for(int j=st[i];j<en[i];++j)
		{
			int t=query(j+1,j),a=j-t+1,b=j+t;
			if(a>st[i] && b<en[i]) ans=max(ans,t*2);
			else if(a==st[i] && b==en[i]) putINF();
			else if(a==st[i]) ans=max(ans,t*2+dfs(b+1,0));
			else ans=max(ans,t*2+dfs(a-1,1));
		}//even
	}
	printf("%d\n",ans);

	return 0;
}

猜你喜欢

转载自blog.csdn.net/Dream_Lolita/article/details/85413193