P1872 回文串計數(回文樹)
題目描述
小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 回文串計數(回文樹)