題解 CF17E 【Palisection】
阿新 • • 發佈:2020-09-19
卡空間PAM,2010沒有PAM,所以都是馬拉車
眾所周知,PAM擁有十分優秀的時間複雜度,但空間複雜度lj得不行
但這題卡空間,所以得用到鄰接連結串列PAM
先講思路
題目要求相交的迴文子串對,這很難做
於是我們求補集,求不相交的迴文子串對,再用總數減即可
求法和上文的最長雙迴文子串 類似
正反建一次PAM,存該位置結尾的迴文子串個數,然後加法改乘法
自己領悟一下,挺簡單的。
現在講一下鄰接連結串列PAM
注意:鄰接連結串列PAM不是使空間變小了,而是用時間換空間
我們記邊結構體\(line\)
存\(3\)個資訊:\(nx,to,w\) 分別表示上一條邊,這條邊通向的節點編號,這條邊是代表哪個字元
陣列\(fir[i]\)表示\(i\)伸出的最後一條邊的編號(頭插式
當我們要尋找\(u\)的\(v\)兒子
我們就像鄰接連結串列一樣找,直到有一條邊的\(w==v\)為止
找不到記得指根
int getson(int u,int v){
for(int i=u;i!=-1;i=l[i].nx)
if(l[i].w==v)return l[i].to;
return -1;
}
建點的時候把邊建上
void insert(int u,int i){ int Fail=getfail(pre,i),ls=getfail(fail[Fail],i); if(getson(fir[Fail],u)==-1){ if(getson(fir[ls],u)==-1)fail[++tot]=0; //找不到指根 else fail[++tot]=getson(fir[ls],u); //找到了 l[++cnt]=(line){fir[Fail],tot,u};fir[Fail]=cnt; //加邊 len[tot]=len[Fail]+2; ans[tot]=ans[fail[tot]]+1; //結尾迴文子串個數 pre=tot; }else pre=getson(fir[Fail],u); }
然鵝事實上你仍然過不了,你還要繼續壓空間,省掉一堆陣列就可以過啦!
總程式碼:
#include<bits/stdc++.h> #define maxn 2000005 #define mod 51123987 using namespace std; char s[maxn]; int slen,b[maxn]; long long res; int fail[maxn],len[maxn],ans[maxn],fir[maxn]; struct line{int nx,to,w;}l[maxn]; int tot,pre,cnt; void init(){ memset(fir,-1,sizeof(fir));cnt=0; fail[0]=1;len[1]=-1;tot=1;pre=0; } int getfail(int x,int i){ while(i-len[x]-1<0||s[i-len[x]-1]!=s[i])x=fail[x]; return x; } int getson(int u,int v){ for(int i=u;i!=-1;i=l[i].nx) if(l[i].w==v)return l[i].to; return -1; } void insert(int u,int i){ int Fail=getfail(pre,i),ls=getfail(fail[Fail],i); if(getson(fir[Fail],u)==-1){ if(getson(fir[ls],u)==-1)fail[++tot]=0; else fail[++tot]=getson(fir[ls],u); l[++cnt]=(line){fir[Fail],tot,u};fir[Fail]=cnt; len[tot]=len[Fail]+2; ans[tot]=ans[fail[tot]]+1; pre=tot; }else pre=getson(fir[Fail],u); } int main(){ int n; scanf("%d",&n); scanf("%s",s);slen=strlen(s);init(); reverse(s,s+slen); for(int i=0;i<slen;i++)insert(s[i]-'a',i),b[slen-i-1]=ans[pre]; for(int i=slen-1;i>=0;i--)b[i]+=b[i+1],b[i]%=mod; reverse(s,s+slen);init(); for(int i=0;i<slen-1;i++){ insert(s[i]-'a',i);int x=ans[pre]; res+=(1ll*x*b[i+1])%mod,res%=mod; } printf("%lld\n",((1ll*b[0]*(b[0]-1)/2ll)%mod-res+mod)%mod); return 0; }