1. 程式人生 > >P1872 回文串計數(回文樹)

P1872 回文串計數(回文樹)

競賽 過多 方法 algorithm mem ret 之前 整數 define

題目描述

小a雖然是一名理科生,但他常常稱自己是一名真正的文科生。不知為何,他對於背誦總有一種莫名其妙的熱愛,這也促使他走向了以記憶量大而聞名的生物競賽。然而,他很快發現這並不能滿足他熱愛背誦的心,但是作為一名強大的OIER,他找到了這麽一個方法——背誦基因序列。然而這實在是太困難了,小啊感覺有些招架不住。不過他發現,如果他能事先知道這個序列裏有多少對互不相交的回文串,他或許可以找到記憶的妙法。為了進一步驗證這個想法,小a決定選取一個由小寫字母構成的字符串SS來實驗。由於互不相關的回文串實在過多,他很快就數暈了。不過他相信,在你的面前這個問題不過是小菜一碟。

(1)對於字符串SS,設其長度為Len,那麽下文用Si表示SS中第i個字符(1<=i<=Len)。

(2)S[i,j]表示SS的一個子串,S[i,j]="SiSi+1Si+2...Sj-2Sj-1Sj",比如當SS為"abcgfd"時,S[2,5]="bcgf",S[1,5]="abcgf"。

(3)當一個串被稱為一個回文串當且僅當將這個串反寫後與原串相同,如“abcba”。

(4)考慮一個四元組(l,r,L,R),當S[l,r]和S[L,R]均為回文串時,且滿足1<=l<=r<=L<=R<=Len時,我們稱S[l,r]和S[L,R]為一對互不相交的回文串。即本題所求,也即為這種四元組的個數。兩個四元組相同當且僅當對應的l,r,L,R都相同。

輸入輸出格式

輸入格式:

輸入僅一行,為字符串SS,保證全部由小寫字母構成,由換行符標誌結束。

50%的數據滿足SS的長度不超過200;

100%的數據滿足SS的長度不超過2000。

輸出格式:

僅一行,為一個整數,表示互不相關的回文串的對數。

輸入輸出樣例

輸入樣例#1: 復制

aaa

輸出樣例#1: 復制

5

說明

【樣例數據說明】

SS="aaa",SS的任意一個字符串均為回文串,其中總計有5對互不相關的回文串:

(1,1,2,2),(1,1,2,3),(1,1,3,3),(1,2,3,3),(2,2,3,3)。


題解

在這裏我們對回文樹引入一個新的標記,dep
它表示的是當前這個節點為回文串時,以它結尾的串中包含的回文串的數量


好,那麽接下來的意思就很簡單了。
我們只需要依次遞推過去,先更新出原本順序上每一位的回文串的數量,再更新出倒序上每一位的回文串的數量(其實倒序就是求尾部的回文串數量)。
這樣我們每更新一位,就加上之前每一位的回文串的數量。再乘以下一位的按倒序處理出來的回文串的數量就ok了。


代碼

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int tot;
ll ans,p1[20001],p2[20001];
struct node{
    int fail,ch[26],len,cnt,dep;
}t[200001];
char s[200001];

int read()
{
    int x=0,w=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*w;
}

void solve1()
{
    int len=strlen(s+1),k=0;s[0]='#';
    t[0].fail=t[1].fail=1;t[1].len=-1;tot=1;
    for(int i=1;i<=len;i++)
    {
        while(s[i-t[k].len-1]!=s[i])k=t[k].fail;
        if(!t[k].ch[s[i]-'a']){
            t[++tot].len=t[k].len+2;
            int j=t[k].fail;
            while(s[i-t[j].len-1]!=s[i])j=t[j].fail;
            t[tot].fail=t[j].ch[s[i]-'a'];
            t[k].ch[s[i]-'a']=tot;
            t[tot].dep=t[t[tot].fail].dep+1;
        }
        k=t[k].ch[s[i]-'a'];
        p1[i]=t[k].dep;
        t[k].cnt++;
    }
}


void solve2()
{
    int len=strlen(s+1),k=0;s[0]='#';
    t[0].fail=t[1].fail=1;t[1].len=-1;tot=1;
    for(int i=1;i<=len;i++)
    {
        while(s[i-t[k].len-1]!=s[i])k=t[k].fail;
        if(!t[k].ch[s[i]-'a']){
            t[++tot].len=t[k].len+2;
            int j=t[k].fail;
            while(s[i-t[j].len-1]!=s[i])j=t[j].fail;
            t[tot].fail=t[j].ch[s[i]-'a'];
            t[k].ch[s[i]-'a']=tot;
            t[tot].dep=t[t[tot].fail].dep+1;
        }
        k=t[k].ch[s[i]-'a'];
        t[k].cnt++;
        p2[len-i+1]=t[k].dep;
    }
}


int main()
{
    scanf("%s",s+1);
    int len=strlen(s+1);
    solve1();
    reverse(s+1,s+len+1);
    memset(t,0,sizeof(t));
    solve2();
    for(int i=1;i<=len;i++)p1[i]+=p1[i-1];
    for(int i=1;i<=len;i++)ans+=p1[i]*p2[i+1];
    printf("%lld",ans);
    return 0;
}

P1872 回文串計數(回文樹)