1. 程式人生 > 實用技巧 >題解 CF17E 【Palisection】

題解 CF17E 【Palisection】

卡空間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;
}