bzoj 4962 簡單的字符串 題解

博客觀賞效果更佳
github
cnblogs

題意簡述

給你一個長度爲 nn 的數組 aa,問你有多少個區間,滿足:

  1. 長度爲偶數
  2. 前一半和後一半循環同構

n5000,ai5000n\le 5000,a_i\le 5000

思路

兩個串 a,ba,b 循環同構 ,那麼一定可以把 aa 分成兩個串 u,vu,v 接起來,然後把 bb 表示成 v,uv,u 的形式。

就比如 a="abcde",b="cdeab"a=\texttt{"abcde"},b=\texttt{"cdeab"},那麼 a="ab"+"cde",b="cde"+"ab"a=\texttt{"ab"+"cde"},b=\texttt{"cde"+"ab"}

然後現在這兩個串相鄰了,也就是有一段連續的 uvvuuv|vu 的形式。考慮枚舉中間這個劃分線,然後計算兩邊有多少滿足條件的。這也許能用哈希水過,但是我們要想一個正經辦法(**)

一般我們遇到這樣的“分塊迴文” 的問題,都是怎麼做的呢⊙(・◇・)?

聯想一下 codeforces 932G 這個題,我們可以把前面一半的 uvuv 反過來,然後一個隔一個的插入到後半邊的 vuvu 裏面去。這樣插入完就會變成兩個長度爲偶數的迴文串拼在一起啦 (/≧▽≦/)

關於它爲啥會變成這樣

先考慮一個串的情況。現在有一個 uu,假設它由三個字符 a,b,ca,b,c 構成。我們把它反過來,然後一個隔一個的插入,變成:

cabbac\color{red} c\color{black}a \color{red} b \color{black} b \color{red} a \color{black} c

新插入的用紅色表示,原來就有的用黑色表示。然後我們發現它變成了一個迴文串!

容易歸納證明,無論它長度多少,這樣子做總會變成一個迴文串。

然後考慮兩個串 u,vu,v 拼一塊的情況。我們把 u+vu+v 反過來,一個隔一個的插入到 v+uv+u 中,會變成什麼呢?

ss'ss 反過來的串。然後顯然 (u+v)=v+u(u+v)'=v'+u'。我們把它插入到 v+uv+u 中之後,前面 v|v| 個串會先變成一個長度爲 2v2|v| 的迴文串,後面還有 u|u| 個串,會變成長度爲 2u2|u| 的迴文串。於是總體就變成了一個長度爲 2v+2u2|v|+2|u| 的兩個偶數長度迴文串。

舉個例子,u="ab",v="cd"u=\texttt{"ab"},v=\texttt{"cd"},這樣插入完之後變成 dccdbaab\color{red}\texttt{d} \color{black} \texttt{c} \color{red} \texttt{c} \color{black} \texttt{d} \color{red} \texttt{b} \color{black} \texttt{a} \color{red} \texttt{a} \color{black} \texttt{b}。 它等於 "dccd"+"baab"\texttt{"dccd"}+\texttt{"baab"}

(你們可能不知道這公式有多難打,就爲了舉個形象的例子… 唉o(╥﹏╥)o)

然後我們只需要用 Manacher 統計一下有多少個這樣的迴文串即可(~ ̄▽ ̄~)

pre[i]pre[i] 表示從 ii 往前最多能跳多少長度,使得跳過的部分是迴文串。然後 f[i]f[i] 是迴文中心。

lastlast 記錄上一個迴文前綴的位置。當前位置在 ii ,如果 [last,i][last,i] 是個迴文串,或者 [ipre[i]+1,i][i-pre[i]+1,i] 是個迴文串,那麼當前的 ii 就是合法的~☆,答案 ++。

那麼如何判斷 [l,r][l,r] 是不是迴文串呢⊙(・◇・)?設 mid=(l+r)/2mid=(l+r)/2,看看是否滿足 f[mid](rl+1)/2f[mid]\ge (r-l+1)/2 即可。

代碼

#include <bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
    #define N 14444
    #define F(i,l,r) for(int i=l;i<=r;++i)
    #define D(i,r,l) for(int i=r;i>=l;--i)
    #define Fs(i,l,r,c) for(int i=l;i<=r;c)
    #define Ds(i,r,l,c) for(int i=r;i>=l;c)
    #define MEM(x,a) memset(x,a,sizeof(x))
    #define FK(x) MEM(x,0)
    #define Tra(i,u) for(int i=G.Start(u),v=G.To(i);~i;i=G.Next(i),v=G.To(i))
    #define p_b push_back
    #define sz(a) ((int)a.size())
    #define iter(a,p) (a.begin()+p)
    int I()
    {
        int x=0;char c=getchar();int f=1;
        while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
        while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        return (x=(f==1)?x:-x);
    }
    void Rd(int cnt,...)
    {
        va_list args; va_start(args,cnt);
        F(i,1,cnt) {int* x=va_arg(args,int*);(*x)=I();}
        va_end(args);
    }
 
    int n; 
    int a[N];
    void Input()
    {
        n=I();
        F(i,1,n) a[i]=I();
    }
 
    int s[N];
    int f[N],pre[N];
    int calc(int p)
    {
        int l=p,r=p+1;
        int cnt=0;
        while(l>=1 and r<=n) s[++cnt]=a[l],s[++cnt]=a[r],--l,++r;
 
        F(i,0,cnt+1) f[i]=pre[i]=0;
        int id=1,Max=1;
        F(i,1,cnt) 
        {
            f[i]=min(max(Max-i,0),f[id+(id-i)]);
            while(i+f[i]+1<=n and i-f[i]>0 and s[i+f[i]+1]==s[i-f[i]]) ++f[i]; 
            if (i+f[i]>=Max) Max=i+f[i],id=i;
            pre[i+f[i]]=max(pre[i+f[i]],f[i]);
        }
        D(i,cnt,1) pre[i]=max(pre[i],pre[i+1]-1);
        Ds(i,cnt,1,i-=2) pre[i]*=2;
 
        int ans=0,last=0;
        Fs(i,2,cnt,i+=2) 
        {
            if (f[i/2]==i/2) last=i;
            if (f[(i+last)/2]>=(i-last)/2 or f[(i-pre[i])/2]>=(i-pre[i])/2) ++ans;
        }
        return ans;
    }
    void Soviet()
    {
        int ans=0;
        F(i,1,n-1) ans+=calc(i);
        printf("%d\n",ans);
    }
 
    #define Flan void
    Flan IsMyWife()
    {
        Input();
        Soviet();
    }
}
int main()
{
    Flandre_Scarlet::IsMyWife();
    getchar();getchar();
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章