题解 CF17E 【Palisection】
阅读原文时间:2023年07月10日阅读:1

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