CF1081H
一个显然错误的想法是分别计算 \(A,B\) 本质不同的回文串个数,然后将两者相乘,这将会多次计算一个字符串。
因此要思考的问题是,为什么会计算多次?
为了解决这个问题,将用到下面数个引理,如果只是想解决这个问题,建议跳过所有证明。
假设所有字符串的下标从 \(0\) 开始。
令 \(x^R\) 表示字符串 \(x\) 翻转,令 \(x^a\) 表示由字符串 \(x\) 连续拼接 \(a\) 次后的字符串。
令 \(t\) 为字符串 \(x\) 的一个 border,当且仅当 \(x\) 长为 \(t\) 的前缀与后缀相同,即 \(x{[0,t-1]}=x{[|x|-t,|x|-1]}\)。
令 \(t\) 为字符串 \(x\) 的一个周期,当且仅当 \(x_{i}=x_{i+t}(0\le i<|x|-t)\)。若 \(t \mid |x| \),则 \(t\) 为 \(x\) 的一个完全周期。可以发现 \(x\) 存在一个长度为 \(t\) 的周期当且仅当 \(x\) 有一个长为 \(|x|-t\) 的 border。
令 $border(x) $ 为 \(x\) 最长的 border。
可以发现,若 \(x\) 是回文串,则他的回文前缀与后缀均为 \(x\) 的 border。
因此 \(border(x)\) 为 \(x\) 的最长回文前缀(后缀)。
证明:
假设 \(a\) 为 \(x\) 的一个 border,即 \(x_i=x_{|x|-a+i}(0\le i\le a-1)\)。且 \(x\) 有长为 \(a\) 的回文前缀 \(x_i=x_{a-1-i}(0\le i\le a-1)\),因此 \(x_i=x_{|x|-i-1}\),满足 \(x\) 为回文串。
若 \(S=xy\),\(x,y\) 为两个非空回文串,则 \((x,y)\) 为 \(S\) 的一组回文分解。
若 \(S=xy\),\(x,y\) 均为回文串且 \(y\) 非空,则 \((x,y)\) 为一组非严格回文分解。
若字符串 \(x\) 的最小完全周期长度 \(\neq |x|\),则称 \(x\) 为循环串。
引理 1:
如果 \(p,q\) 均为 \(s\) 的一个周期,且 \(p+q\le |s|\),则 \(\gcd(p,q)\) 同样也是 \(s\) 的一个周期。
证明:
假设 \(p<q,d=q-p\)
\(s_i=s_{i-p}=s_{i+d}(|s|-q\le i<|s|-d)\).
\(s_i=s_{i+q}=s_{i+q-p}=s_{i+d}(0\le i<|s|-q)\).
因此 \(q-p\) 也是一个周期,根据欧几里得算法可以得到 \(\gcd(p,q)\) 同样也是一个周期。
引理 2:
令 \(S\) 表示字符串 \(s\) 所有 \(\le \frac{|s|}{2}\) 周期的集合,如果 \(S\) 非空,则 \(\forall u\in S,\min S\mid u\)。
证明:
令 \(v=\min S\),显然 \(u+v\le|s|\),根据引理 1,\(\gcd(u,v)\) 同样属于集合 \(S\),所以 \(\gcd(u,v)\ge v,v\mid u\)。
引理 3:
若 \(S=x_1x_2=y_1y_2=z_1z_2,|x_1|<|y_1|<|z_1|\),\(x_2,y_1,y_2,z_1\) 均为非空回文串,则 \(x_1,z_2\) 均为回文串。
证明:
令 \(z_1=y_1v\),显然 \(v^R\) 为 \(y_2\) 的一个后缀,同样也是 \(x_2\) 的一个后缀。
因为 \(x_2\) 为回文串,因此 \(v\) 为 \(x_2\) 的前缀,所以 \(x_1v\) 是 \(z_1\) 的一个前缀。
又因为 \(y_1\) 是 \(z_1\) 的回文前缀,所以 \(|v|\) 为 \(z_1\) 的一个周期,即 \(|v|\) 为 \(x_1v\) 的一个周期。
假设 \(t\) 为一个任意大的数,即 \(x_1\) 为 \(v^t\) 的后缀。
因为 \(v^R\) 为 \(z_1\) 的前缀,\(x_1\) 为 \((v^R)^t\) 的前缀。所以 \(x_1^R\) 为 \(v^t\) 的一个后缀,因此 \(x_1\) 为回文串。
引理 4:
若串 \(S\) 存在回文分解,令 \(a\) 表示 \(S\) 的最长回文前缀,\(S=ax\),\(b\) 表示 \(S\) 的最长回文后缀,\(S=yb\),则 \(x,y\) 中必然有一个是回文串。
证明:
假设 \(x,y\) 均不是回文串,令 \(p,q\) 为 \(S\) 的一个回文分解,因此 \(S=yb=pq=ax\),根据引理 3,这显然不成立。
引理 5:
若 \(S=p_1q_1=p_2q_2\)(\(|p_1|<|p_2|\),且 \(p_1,p_2,q_1,q_2\) 均为回文,\(q_1,q_2\) 非空),则 \(S\) 是一个循环串。
证明:
可以发现 \(\gcd(|p_2|-|p_1|,|S|)\) 是 \(S\) 的一个周期。
因为 \(p_1\) 是 \(p_2\) 的一个回文前缀,所以 \(p_2,q_1\) 均有周期 \(|p_2|-|p_1|\)。由于 \([p_1,p_2-1]\) 这部分被重复覆盖,因此 \(S\) 也有周期 \(|p_2|-|p_1|\)。
\(\forall x\in[0,t),S_x=S_{|p_2|-1-x}=S_{|S|-1+|p_1|-(|p_2|-1-x)}=S_{|S|-|p_2|+|p_1|+x}\)
因此 \(|S|-(|p_2|-|p_1|)\) 为 \(S\) 的一个周期。根据引理 1,\(\gcd(|p_2|-|p_1|,|S|)\mid |S|\),所以 \(|S|\) 是一个循环串。
引理 6:
令 \(S=p_1q_1=p_2q_2=\cdots=p_tq_t\) 是所有 \(S\) 的非严格分解,令 \(h\) 表示最小完全周期。若 \(t\ne 0\),则 \(t=\frac{|S|}{h}\)。
证明:
根据引理 5,当 \(t=1\) 时 \(h=|S|\),于是接下来只讨论 \(t\ge 2\)。
令 \(\alpha =S[0,h-1]\),显然 \(\alpha\) 并不是循环串(否则 \(S\) 就会有一个更小的完全周期),因此 \(\alpha\) 最多有一个非严格分解。
令 \(S=pq\) 为一组非严格分解,存在 \(\max(|p|,|q|)\ge h\)。
当 \(|p|\ge h\) 时,\(\alpha=p[0,h-1]\),因为 \(\alpha[0,|p|\bmod h-1]=p^R[0,|p|\bmod h-1]=(\alpha[0,|p|\bmod h-1])^R\),因此 \(\alpha[0,|p|\bmod h-1]\) 为回文串。类似的,\(\alpha[|p|\bmod h-1,h-1]\) 同样也是回文的。
\(|q|\ge h\) 时同理。所以 \(\alpha[0,|p|\bmod h-1],\alpha[|p|\bmod h-1,h-1]\) 为 \(\alpha\) 的非严格回文分解。
因此 \(\alpha\) 具有非严格回文分解,假设唯一的非严格回文分解为 \(\alpha[0,g-1],\alpha[g,h-1]\)。所以所有 \(p_i\) 必须满足 \(p_i\bmod h=g\),所以 \(t\le \frac{|S|}{h}\),并且这 \(\frac{|S|}{h}\) 种 \(p_i\) 都是合法的。
引理 7:
令 \(S=p_1q_1=p_2q_2=\cdots=p_tq_t(|p_i|<|p_{i+1}|)\) 是所有 \(S\) 的非严格分解。则 \(\forall i \in [1,t-1]\),\(p_i=border(p_{i+1})\) 与 \(q_{i+1}=border(q_i)\) 至少有一项成立。
至少有一项成立。
证明:
先直接考虑如何求出所有非严格回文分解。
假设 \(S\) 的最长回文前缀为 \(a(a\ne S),S=ax\),最长回文后缀为 \(b\)(\(b\) 有可能为 \(S\))\(S=yb\)。
若 \(x=b\),显然 \(S=ab\) 为唯一的分解方法。
若 \(S=pq\) 并且 \(p\ne a,q\ne b\),\(p,q\) 均为回文,根据引理 3,可以得到 \(x,y\) 均为回文串。
因此若 \(x,y\) 均不为回文,则 \(S\) 不存在非严格回文分解
若 \(x,y\) 中只有一个为回文串,则 \(S\) 只存在唯一一种非严格回文分解。
若 \(x,y\) 均为回文串,然后可以找到第二长的非自回文前缀 \(c,S=cz\),若 \(z\) 不是回文串或 \(c=y\),则 \(S=ax=yb\) 是唯二合法的分解。
否则 \(z\) 是回文串,可以找出所有长度介于 \(|z|\) 与 \(b\) 之间的回文后缀,它们对应的前缀也一定是回文的(引理 3)。因此就求出了所有非严格回文分解(由于回文后缀长度不会大于 \(|b|\),回文前缀长度不会大于 \(|a|\),因此这样求出的回文分解是不漏的)
重新考虑如何证明,对于非 \(S=ax=yb\) 这种情况,可以发现对于 \((c,z),(a,x)\) 满足 \(p_i=border(p_{i+1})\),其余均满足 \(q_i=border(q_{i+1})\)。
然后考虑如何证明 \(S=ax=yb\) 这种情况引理依然成立。
先假设这种情况是错误的,令 \(p=border(a),s=border(b),S=ax=pq=rs=yb\)(\(|a|>|p|>|y|,|a|>|r|>|y|\),\(p,s\) 均为回文串)
根据引理 6,可以得到 \(S\) 只有两种非严格回文分解,且 \(S=\alpha^2\)。
若 \(|p|\ge |\alpha|\),有 \(q=\alpha[|p|\bmod h,h-1]\),因此 \(q\) 也是一个回文串,矛盾。
因此有 \(|p|<|\alpha|\),同样可以得到 \(|s|<|\alpha|\),令 \(\alpha=pq'=r's\),并且根据引理 6,令 \(\alpha\) 唯一的非严格分解为 \(\beta\theta\)。因此根据引理 3 可得到 \(q'\) 和 \(r‘\) 也是一个回文串,矛盾。
根据引理 7,假设 \(S=xy\),如果用 \(border(x)\) 代替 \(x\) 或 \(border(y)\) 代替 \(y\) 后也能拼出 \(S\),那么 \(S\) 将会被重复计算,答案需要减 1。
所以对于 \(A\) 中的一个回文串 \(x\),我们需要计算从 \(x\) 和 \(border(x)\) 中都可以拼出的字符串 \(S\) 并从答案中减去。其中找 \(x\) 和 \(border(x)\) 都可以通过回文树完成。
令 \(x=border(x)w\),需要计算的就是在 \(B\) 中有多少 \(T\) 满足 \(T=wS\),且 \(S,T\) 均为 \(B\) 中的回文串。因为 \(|w|\) 是 \(x\) 的最短周期,因此 \(w\) 不是循环串。
若 \(|w|>|S|,w=S+U\),其中 \(S,U\) 均为回文串。且因为 \(w\) 不是一个循环串,所以 \(w\) 最多有一个非严格回文分解(引理5)。我们只需验证这个非严格回文分解是否包含 \(w\) 的最长前缀和后缀,然后通过 hash 验证 \(S\) 和 \(wS\) 是否在 \(B\) 中存在即可。
若 \(|w|\le |S|\),若 \(S\) 不是 \(T\) 的最长回文后缀,则 \(w\) 一定是循环串(引理 2),矛盾。
因此只需要对所有最长回文后缀(即 \(S=border(T)\) 且 \((|T|-|S|\times 2 \le |T|\))的位置判断一遍即可,可以通过 hash 实现。
找子串的最大回文前缀和后缀可以通过对两棵回文树(一棵正常和一棵翻转)倍增,令 \(up_{i,j}\) 表示从节点 \(i\) 跳 fail 链 \(2^j\) 步后的节点。在查询 \(s[l,r]\) 的最大回文后缀时,找到对应 \(s[1,r]\) 的最大回文后缀的节点。如果就是包含与 \([l,r]\)区间,这就是答案。否则,从大到小枚举枚举 \(j\) 找合适的即可。
然后再对 \(B\) 同样做一遍,注意需要加上既会被 \(border(x)\) 计算,又会被 \(border(y)\) 计算的这部分。
#include<bits/stdc++.h>
#define ll long long
#define qwq printf("qwq\n");
#define R(i,a,b) for(int i=(a),i##E=(b);i<=i##E;i++)
#define L(i,a,b) for(int i=(b),i##E=(a);i>=i##E;i--)
using namespace std;
const int base1=1379973;
const int base2=3771931;
const int mod1=1e9+7;
const int mod2=1e9+9;
int pw1[222222],pw2[222222];
char A[222222],B[222222];
int n,m;
ll ans;
struct hashing
{
    ll x,y;
    inline hashing (ll X=0,ll Y=0):x(X),y(Y) {}
    inline hashing nxt(int a)
    {
        return hashing(x*pw1[a]%mod1,y*pw2[a]%mod2);
    }
    inline hashing operator +(const char a)
    {
        return hashing((x+a)%mod1,(y+a)%mod2);
    }
    inline hashing operator +(const hashing a)
    {
        return hashing((x+a.x)%mod1,(y+a.y)%mod2);
    }
    inline hashing operator -(const hashing a)
    {
        return hashing((x-a.x+mod1)%mod1,(y-a.y+mod2)%mod2);
    }
    inline ll val()
    {
        return x*mod2+y;
    }
};
const ll hs=1235287;
struct hashtable
{
    ll head[hs+10],cnt;
    struct edge
    {
        ll nxt;
        ll key,val;
    }e[666666];
    inline ll hash(ll x)
    {
        return (x%hs+hs)%hs;
    }
    inline void add_edge(ll u,ll v,ll d)
    {
        e[++cnt]=(edge){head[u],v,d};head[u]=cnt;
    }
    inline int get_val(ll x)
    {
        ll h=hash(x);
        for(int i=head[h];i;i=e[i].nxt) 
            if(e[i].key==x) return e[i].val;
        return 0;
    }
    inline void ins(ll x,ll d)
    {
        ll h=hash(x);
        for(int i=head[h];i;i=e[i].nxt)
        {
            if(e[i].key==x) 
            {
                e[i].val=d;
                return;
            }
        } 
        add_edge(h,x,d);
    }
};
struct PalindromicTree
{
    hashtable mxlen,hst;
    hashing hs1[222222],hs2[222222];
    char s[222222];
    int n,tot,to[222222][26];
    int fil[222222],diff[222222],slink[222222],len[222222];
    int mnp[222222];
    ll phs1[222222],phs2[222222];
    pair<int,int>seg[222222];
    int append(int n,int p)
    {
        int c=s[n]-'a';
        while(s[n-1-len[p]]!=s[n]) p=fil[p];
        if(to[p][c]) return to[p][c];
        int q=++tot;
        len[q]=len[p]+2;
        seg[q]={n-len[q]+1,n};
        int f=fil[p];
        while(s[n-1-len[f]]!=s[n]) f=fil[f];
        fil[q]=to[f][c];
        to[p][c]=q;
        diff[q]=len[q]-len[fil[q]];
        slink[q]=diff[q]==diff[fil[q]]?slink[fil[q]]:fil[q];
        return q;
    }
    int st[222222];
    ll geths2(int l,int r,int l2,int r2)
    {
        return ((hs1[r]-hs1[l-1].nxt(r-l+1)).nxt(r2-l2+1)+hs1[r2]-hs1[l2-1].nxt(r2-l2+1)).val();
    }
    ll geths(int l,int r)
    {
        return (hs1[r]-hs1[l-1].nxt(r-l+1)).val();
    }
    ll gethss(int l,int r)
    {
        return (hs2[l]-hs2[r+1].nxt(r-l+1)).val();
    }
    void construct(int _n,char _s[])
    {
        n=_n;
        hs1[0]=hashing(0,0);
        R(i,1,n) s[i]=_s[i],hs1[i]=hs1[i-1].nxt(1)+s[i];
        hs1[n+1]=hashing(0,0);
        L(i,1,n) hs2[i]=hs2[i+1].nxt(1)+s[i];
        int p=0;
        fil[0]=1,len[0]=0,seg[0]={1,0};
        fil[1]=0,len[1]=-1,seg[1]={1,0};
        tot=1;
        st[0]=p;
        R(i,1,n)
        {
            p=append(i,p);
            st[i]=p;
        }
        R(i,2,tot)
        {
            hst.ins(geths(seg[i].first,seg[i].second),1);
            mnp[i]=len[i]-len[fil[i]];
            phs1[i]=geths(seg[i].first,seg[i].first+mnp[i]-1);
            phs2[i]=gethss(seg[i].first,seg[i].first+mnp[i]-1);
            if(len[i]>=mnp[i]*2) mxlen.ins(phs1[i],max(mxlen.get_val(phs1[i]),len[i]/mnp[i]-1));
        }
    }
    int query_mxp(int p,int b)
    {
        if(b>len[p]) return len[p];
        while(1)
        {
            int l=len[slink[p]];
            int d=diff[p];
            if(b>=l) return ((b-l)/d)*d+l;
            p=slink[p];
        }
    }
}t1,t2,t11,t22;
void init()
{
    t1.construct(n,A);
    t2.construct(m,B);
    ans=1ll*(t1.tot-1)*(t2.tot-1);
    reverse(A+1,A+n+1);
    reverse(B+1,B+m+1);
    t11.construct(n,A);
    t22.construct(m,B);
    reverse(A+1,A+n+1);
    reverse(B+1,B+m+1);
}
inline bool is_pal(int l,int r)
{
    return t1.query_mxp(t1.st[r],r-l+1)==r-l+1;
}
void check_pal_deco(int l,int r)
{
    int p=t1.query_mxp(t1.st[r],r-l+1);
    if(l<=r-p&&r-p<r&is_pal(l,r-p))
    {
        ans-=t2.hst.get_val(t1.geths2(l,r,l,r-p));
        return;
    }
    p=t11.query_mxp(t11.st[n-l+1],r-l+1);
    if(l<l+p&&l+p<=r&&is_pal(l+p,r))
    {
        ans-=t2.hst.get_val(t1.geths2(l,r,l,l+p-1));
        return;
    }
}
unordered_map<ll,int>mp;
void calc(int opt)
{
    R(i,2,t1.tot)
    {
        int p=t1.mnp[i];
        if(t1.len[i]<=p) continue;
        int cnt=t2.mxlen.get_val(t1.phs2[i]);
        ans-=cnt;
        check_pal_deco(t1.seg[i].second-p+1,t1.seg[i].second);
        if(!opt) mp[t1.phs1[i]]++;
        else ans+=mp[t1.phs2[i]];
    }
}
signed main()
{
    pw1[0]=pw2[0]=1;
    R(i,1,200005) pw1[i]=1ll*pw1[i-1]*base1%mod1,pw2[i]=1ll*pw2[i-1]*base2%mod2;
    scanf("%s%s",A+1,B+1);
    n=strlen(A+1),m=strlen(B+1);
    init();
    calc(0);
    swap(A,B);
    swap(n,m);
    swap(t1,t2);
    swap(t11,t22);
    calc(1);
    printf("%lld\n",ans);
}

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号