【BZOJ4166】月宫的符卡序列
题解:题倒不难,就是有点恶心。
首先学习回文串的时候一定学到了这样一个结论:一个长度为n的串的本质不同的回文子串数量不超过n个。
那么我们就可以试图将所有回文串的价值都计算出来,这就需要我们先计算出每个回文中心i的最长回文半径rl[i],那么那些半径在[1,rl[i]]中的,且以i为回文中心的回文串的价值都应该被更新。其实只需要更新最长的那个就行,其余的可以扫一遍回文树,逐层更新上去。但是回文树太大建不出来怎么办?我们可以用hash,直接通过hash值得到每个串在回文树上的父亲。这样就可以更新了。
但是hash要用到map啊,然后本人就被光荣的卡常了。于是采取了一点黑科技~
#include <cstdio> #include <cstring> #include <iostream> #include <vector> using namespace std; typedef long long ll; const int maxn=2000010; vector<int> v[maxn]; const ll m1=9997957; const ll m2=1000000007; int n,tot,len,ml,pos,mx; char str[maxn],ss[maxn]; int rl[maxn],ans[maxn],p[maxn]; int head[10000000],next[maxn]; ll h1[maxn],b1[maxn],h2[maxn],b2[maxn],val[maxn]; int find(ll t1,ll t2) { for(int i=head[t1];i;i=next[i]) if(val[i]==t2) return i; return 0; } void add(ll t1,ll t2) { val[++tot]=t2,next[tot]=head[t1],head[t1]=tot; } void work() { n=tot=mx=0; memset(ans,0,sizeof(ans)); memset(rl,0,sizeof(rl)); memset(head,0,sizeof(head)); scanf("%s",ss),len=strlen(ss); int i,j,k,t; ll t1,t2; str[n++]='*'; for(i=0;i<len;i++) str[n++]=ss[i],str[n++]='*'; for(ml=-1,i=0;i<n;i++) { if(i<ml) rl[i]=min(ml-i+1,rl[2*pos-i]); else rl[i]=1; for(;rl[i]<=i&&i+rl[i]<n&&str[i+rl[i]]==str[i-rl[i]];rl[i]++); if(i+rl[i]-1>ml) pos=i,ml=i+rl[i]-1; } for(b1[0]=b2[0]=1,i=1;i<=n;i++) b1[i]=b1[i-1]*233%m1,b2[i]=b2[i-1]*2333%m2; for(i=0;i<n;i++) { if(i) h1[i]=h1[i-1]*233%m1,h2[i]=h2[i-1]*2333%m2; h1[i]=(h1[i]+str[i])%m1,h2[i]=(h2[i]+str[i])%m2; } for(i=0;i<n;i++) { if(rl[i]==1) continue; t1=(h1[i+rl[i]-1]-h1[i-1]*b1[rl[i]]%m1+m1)%m1; t2=(h2[i+rl[i]-1]-h2[i-1]*b2[rl[i]]%m2+m2)%m2; t=find(t1,t2); if(t) { ans[t]^=(i-1>>1); continue; } add(t1,t2),ans[tot]=(i-1>>1),p[tot]=i,v[rl[i]-1].push_back(tot); } for(i=len;i;i--) { for(j=0;j<v[i].size();j++) { k=v[i][j],mx=max(mx,ans[k]); if(i==1) continue; t1=(h1[p[k]+i-1]-h1[p[k]-1]*b1[i]%m1+m1)%m1; t2=(h2[p[k]+i-1]-h2[p[k]-1]*b2[i]%m2+m2)%m2; t=find(t1,t2); if(t) { ans[t]^=ans[k]; continue; } add(t1,t2),ans[tot]=ans[k],p[tot]=p[k],v[i-1].push_back(tot); } v[i].clear(); } printf("%d ",mx); } int main() { int T; scanf("%d",&T); while(T--) work(); return 0; }