题解 P3900 【[湖南集训]图森】
xtx1092515503 · · 题解
我们先考虑一个暴力的解法:暴力枚举回文串的对称轴(可能是某个字符串的单个字符,也有可能是两个字符中间的空格,甚至有可能是两个不同串首尾拼接在一起的接口),然后向两边扩张,直到不能再扩为止。
显然,除了一开始的初始状态以外,这个对称轴是没有意义的——我们只需要记录当前回文串最左端的位置和最右端的位置即可。
我们将所有串全都拼在一起,成为一个大串
设
则有
其中,
显然,上面的转移式中,
显然,这个DP的复杂度是
我们发现,一个
因此我们求出这个长度之后,
则我们现在就可以把维数消减掉一维了。我们设
则我们在转移的时候,只需要枚举新拼接上的那个串是什么,然后求出它最终延伸的长度即可转移。
这一段是使用记忆化搜索实现的DP:
int dfs(int x,bool tp){//tp=0:x is the starting point of a suffix;tp=1:the ending point of a prefix.(can refer to PAL function)
if(vis[x][tp]){puts("Infinity");exit(0);}//a circle occurs, and the answer equals inf.
if(f[x][tp]!=-1)return f[x][tp];//memorized searching
vis[x][tp]=true;
int &now=f[x][tp];now=0;
if(!tp){
for(int i=1;i<=all;i++){//numerate the string we're going to concatenate.
int len=PAL(x,ed[i]);//get the maximum palindrome length
int L=ed[i]-len+1,R=x+len-1;
if(L!=st[i]&&R!=ed[id[x]])now=max(now,len<<1);//no string can reach to the end,so k is the maximal parlindrome length
else if(L==st[i])now=max(now,(len<<1)+dfs(R+1,false));//the whole string i is matched
else if(R==ed[id[x]])now=max(now,(len<<1)+dfs(L-1,true));//the whole string id[x] is matched
else{puts("Infinity");exit(0);}//both strings are matched, which means there is a palindrome string, and the answer is inf.
}
}else{//similar to the above function
for(int i=1;i<=all;i++){
int len=PAL(st[i],x);
int L=x-len+1,R=st[i]+len-1;
if(L!=st[id[x]]&&R!=ed[i])now=max(now,len<<1);
else if(L==st[id[x]])now=max(now,(len<<1)+dfs(R+1,false));
else if(R==ed[i])now=max(now,(len<<1)+dfs(L-1,true));
else{puts("Infinity");exit(0);}
}
}
vis[x][tp]=false;
return now;
}
首先,如果你到了一个位置,发现它成环了,则我们只需要在这个环上不停地绕,回文串的长度就会不断增加,因此答案就是
注意到那个PAL(x,y)函数,这是一个用来求当
然后在主函数里我们只需要枚举对称轴即可。显然,这个对称轴数量是
则总复杂度为
代码(附详细英文版注释):
#include<bits/stdc++.h>
using namespace std;
const int N=200100;
int all,st[110],ed[110],id[N],res;
namespace Suffix_Array{
int x[N],y[N],buc[N],sa[N],ht[N],rk[N],n,m;
char s[N];
bool mat(int a,int b,int k){
if(y[a]!=y[b])return false;
if((a+k<=n)^(b+k<=n))return false;
if((a+k<=n)&&(b+k<=n))return y[a+k]==y[b+k];
return true;
}
void SA(){
for(int i=1;i<=n;i++)buc[x[i]=s[i]]++;
for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
for(int i=n;i;i--)sa[buc[x[i]]--]=i;
for(int k=1;k<=n;k<<=1){
int num=0;
for(int i=n-k+1;i<=n;i++)y[++num]=i;
for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
for(int i=0;i<=m;i++)buc[i]=0;
for(int i=1;i<=n;i++)buc[x[y[i]]]++;
for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
for(int i=n;i;i--)sa[buc[x[y[i]]]--]=y[i];
swap(x,y);
x[sa[1]]=num=1;
for(int i=2;i<=n;i++)x[sa[i]]=mat(sa[i],sa[i-1],k)?num:++num;
if(n==num)break;
m=num;
}
for(int i=1;i<=n;i++)rk[sa[i]]=i;
for(int i=1,k=0;i<=n;i++){
if(rk[i]==1)continue;
if(k)k--;
int j=sa[rk[i]-1];
while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++;
ht[rk[i]]=k;
}
}
}
using namespace Suffix_Array;
namespace Sparse_Table{
int LG[N],mn[N][20];
void ST(){
for(int i=2;i<=n;i++)LG[i]=LG[i>>1]+1;
for(int i=1;i<=n;i++)mn[i][0]=ht[i];
for(int j=1;j<=LG[n];j++)for(int i=1;i+(1<<j)-1<=n;i++)mn[i][j]=min(mn[i][j-1],mn[i+(1<<(j-1))][j-1]);
}
int LCP(int x,int y){//ask the LCP of two suffixes
if(x==y)return ed[id[x]]-st[id[x]]+1;//the LCP of two same strings is the string itself
x=rk[x],y=rk[y];
if(x>y)swap(x,y);
x++;
int k=LG[y-x+1];
return min(mn[x][k],mn[y-(1<<k)+1][k]);
}
int PAL(int x,int y){//return the maximal length of a common string that is both a suffix starting from index x and a prefix ending at index y
return min(LCP(x,n-y+1),min(ed[id[x]]-x+1,y-st[id[y]]+1));//can't go over the length of original strings
}
}
using namespace Sparse_Table;
bool vis[N][2];//use the vis array to avoid circles
int f[N][2];
int dfs(int x,bool tp){//tp=0:x is the starting point of a suffix;tp=1:the ending point of a prefix.(can refer to PAL function)
if(vis[x][tp]){puts("Infinity");exit(0);}//a circle occurs, and the answer equals inf.
if(f[x][tp]!=-1)return f[x][tp];//memorized searching
vis[x][tp]=true;
int &now=f[x][tp];now=0;
if(!tp){
for(int i=1;i<=all;i++){//numerate the string we're going to concatenate.
int len=PAL(x,ed[i]);//get the maximum palindrome length
int L=ed[i]-len+1,R=x+len-1;
if(L!=st[i]&&R!=ed[id[x]])now=max(now,len<<1);//no string can reach to the end,so k is the maximal parlindrome length
else if(L==st[i])now=max(now,(len<<1)+dfs(R+1,false));//the whole string i is matched
else if(R==ed[id[x]])now=max(now,(len<<1)+dfs(L-1,true));//the whole string id[x] is matched
else{puts("Infinity");exit(0);}//both strings are matched, which means there is a palindrome string, and the answer is inf.
}
}else{//similar to the above function
for(int i=1;i<=all;i++){
int len=PAL(st[i],x);
int L=x-len+1,R=st[i]+len-1;
if(L!=st[id[x]]&&R!=ed[i])now=max(now,len<<1);
else if(L==st[id[x]])now=max(now,(len<<1)+dfs(R+1,false));
else if(R==ed[i])now=max(now,(len<<1)+dfs(L-1,true));
else{puts("Infinity");exit(0);}
}
}
vis[x][tp]=false;
return now;
}
int main(){
scanf("%d",&all);
for(int i=1;i<=all;i++){
scanf("%s",s+n+1);
m=strlen(s+n+1);
st[i]=n+1;
for(int j=n+1;j<=n+m;j++)s[j]=s[j]-'a'+1,id[j]=i;
n+=m;
ed[i]=n;//st is the starting index after concatenating all the strings,while ed is the ending index
}
for(int i=1;i<=n;i++)s[2*n-i+1]=s[i];//reverse the string and concatenating it after the original string
n<<=1,m=26,SA(),ST();
// for(int i=1;i<=all;i++)printf("(%d,%d)",st[i],ed[i]);puts("");
// for(int i=1;i<=n;i++)printf("%d ",s[i]);puts("");
// for(int i=1;i<=n;i++)printf("%d ",rk[i]);puts("");
memset(f,-1,sizeof(f));
for(int i=1;i<=all;i++){
res=max(res,max(dfs(st[i],false),dfs(ed[i],true)));//palindromes that symmetrix axis is between two different strings
for(int j=st[i];j<=ed[i];j++){//palindromes that symmetric axis is a character
int len=PAL(j,j);//the maximal palindrome length
int L=j-len+1,R=j+len-1;
if(L!=st[i]&&R!=ed[i])res=max(res,(len<<1)-1);//minus one, as the symmetrix axis is calculated twice
else if(L==st[i])res=max(res,(len<<1)-1+dfs(R+1,false));
else if(R==ed[i])res=max(res,(len<<1)-1+dfs(L-1,true));
else{puts("Infinity");exit(0);}
}
for(int j=st[i];j<ed[i];j++){//palindromes that symmetric axis is the blank between two characters
int len=PAL(j+1,j);
int L=j-len+1,R=(j+1)+len-1;
if(L!=st[i]&&R!=ed[i])res=max(res,len<<1);
else if(L==st[i])res=max(res,(len<<1)+dfs(R+1,false));
else if(R==ed[i])res=max(res,(len<<1)+dfs(L-1,true));
else{puts("Infinity");exit(0);}
}
}
printf("%d\n",res);
return 0;
}