Codechef PalindromenessとSHOI2011ダブル回文

Palindromeness

私たちは、次のように文字列のpalindromenessを定義してみましょう:

  • 文字列が回文でない場合は、そのpalindromenessはゼロです。
  • 1文字の文字列のpalindromenessがある1
  • 文字列のpalindromness S一つよりも長さの大きい1 +「[|最初によって形成される文字列のpalindromeness S | / 2]のシンボルS」。

あなたは、文字列与えられているSをすべての空でない部分文字列のpalindromenessesの合計探しSを(すなわちS [i..j]ここで、I <= J)。言い換えれば、のpalindromenessesの和を計算する必要がN *(N + 1)/ 2のサブストリングSNがの長さSを

  • 1| S | 10 5

問題の解決策

ウェンWentao「回文ツリーとその応用。」

溶液

\(len_i = 1 \)特殊処理\(さえhalf_i = \)

CO int N=100000+10;

namespace PAM{
    int str[N],n;
    int last,tot;
    int ch[N][26],fa[N],len[N];
    int half[N],val[N],siz[N];
    
    void init(){
        memset(str,-1,sizeof str),n=0;
        last=tot=1;
        memset(ch,0,sizeof ch);
        fa[0]=fa[1]=1,len[0]=0,len[1]=-1;
        memset(siz,0,sizeof siz);
    }
    int get_fail(int x){
        while(str[n-len[x]-1]!=str[n]) x=fa[x];
        return x;
    }
    void extend(int c){
        int p=get_fail(last);
        if(!ch[p][c]){
            int cur=++tot;
            len[cur]=len[p]+2;
            fa[cur]=ch[get_fail(fa[p])][c];
            ch[p][c]=cur;
            if(len[cur]==1) half[cur]=0;
            else{
                int q=half[p];
                while(str[n-len[q]-1]!=str[n] or
                      2*(len[q]+2)>len[cur]) q=fa[q];
                half[cur]=ch[q][c];
            }
            val[cur]=1+(len[cur]/2==len[half[cur]]?val[half[cur]]:0);
        }
        last=ch[p][c];
        ++siz[last];
    }
    LL main(){
        for(int i=tot;i>=2;--i) siz[fa[i]]+=siz[i];
        LL ans=0;
        for(int i=tot;i>=2;--i) ans+=(LL)siz[i]*val[i];
        return ans;
    }
}

char str[N];

void real_main(){
    scanf("%s",str+1);
    int n=strlen(str+1);
    PAM::init();
    for(int i=1;i<=n;++i){
        PAM::str[++PAM::n]=str[i]-'a';
        PAM::extend(str[i]-'a');
    }
    printf("%lld\n",PAM::main());
}
int main(){
    for(int T=read<int>();T--;) real_main();
    return 0;
}

ダブル回文

SはWW場合に書くことができるR&LT WW R&LT形は二重のパリンドロームと呼ばれています。

Sの長さは、それはパリンドロームの二つの半分前後で同じであるが、4の倍数です。

与えられた文字列の場合は、その最長の回文構造の二重文字列の長さを計算します。

N <= 500000

yybの問題に対する解決策

私たちは回文シーケンスの独自の半分の長さを簡単に転送することができることを見出しました。

各ノードについて、我々は最長の長さを示すために、半分を維持し、その長さは、その祖先ノードの半分を超えません

この方法では、唯一の半分の長さは、現在のポイントが半分であるかどうかを確認するために決定され、文字列の現在の長さが4のようなの倍数であります

ライン上にpでジャンプする半分の半分を見つけるために。

co int N=500000+10;
char s[N];
int last=1,tot=1;
int ch[N][26],fa[N]={1,1},len[N]={0,-1},half[N];

int get_fa(int x,int i){
    while(s[i-len[x]-1]!=s[i]) x=fa[x];
    return x;
}
void extend(int i){
    int p=get_fa(last,i);
    int x=ch[p][s[i]-'a'];
    if(!x){
        x=++tot;
        fa[x]=ch[get_fa(fa[p],i)][s[i]-'a'];
        len[x]=len[p]+2;
        ch[p][s[i]-'a']=x;
        if(len[x]==1) half[x]=0;
        else{
            int q=half[p];
            while(s[i-len[q]-1]!=s[i]||(len[q]+2)<<1>len[x]) q=fa[q];
            half[x]=ch[q][s[i]-'a'];
        }
    }
    last=x;
}
int main(){
    int n=read<int>();
    scanf("%s",s+1);
    for(int i=1;i<=n;++i) extend(i);
    int ans=0;
    for(int i=1;i<=tot;++i)
        if(len[half[i]]<<1==len[i]&&len[i]%4==0)
            ans=max(ans,len[i]);
    printf("%d\n",ans);
    return 0;
}

おすすめ

転載: www.cnblogs.com/autoint/p/11419611.html