【迴文樹 && 求每個迴文串出現次數】HYSBZ
阿新 • • 發佈:2018-12-24
Step1 Problem:
給你一個字串 s,求迴文子串長度 * 該回文串子出現次數的最大值。
Step2 Ideas:
結論:一個字串 s,其長度為 len,該字串本質不同的迴文子串個數不會超過 len.
感性理解一下:增加一個字元最多增加一個新的本質不同的迴文串,因為所有以它為結尾的字尾迴文串,之前都出現過了。
迴文樹:
0是偶點,1是奇點,其他的都是新的迴文串。
以 0 為根的子樹所有的點都是長度為偶數的迴文串。
以 1 為根的子樹所有的點都是長度為奇數的迴文串。
當前點的 fail,指向等於最長字尾迴文的字首點。
因為每個點的字尾迴文串的出現次數都少增加了。
我們以葉子點往父親方向,按照拓撲序,更新字尾迴文串出現的次數。
Step3 Code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 3e5+100;
struct node
{
int len, cnt;//該回文串的長度,該回文串出現的次數
int next[26], fail;//只有26個小寫字母
}a[N];
int top, last;
char s[N];
void Init_Tr()//初始化很重要
{
top = 1, last = 0;
a[0].len = 0, a[1].len = -1;
a[0 ].fail = 1;
}
int i;//減少傳參可以優化很大的時間複雜度
int get_id(int now)
{
while(s[i] != s[i-a[now].len-1]) now = a[now].fail;//判斷是否滿足迴文
return now;
}
void Insert()
{
int len = strlen(s+1);
for(i = 1; i <= len; i++) {
int t = s[i]-'a';
int id = get_id(last);
if(!a[id].next[t]) {
a[++top].len = a[id].len + 2 ;//每次前後各增加一個點
a[top].fail = a[get_id(a[id].fail)].next[t];
a[id].next[t] = top;
}
last = a[id].next[t];//
a[last].cnt++;
}
}
ll solve()
{
ll ans = 0;
for(int i = top; i >= 2; i--) {//從後往前遍歷相當於拓撲序
a[a[i].fail].cnt += a[i].cnt;
ans = max(ans, 1LL*a[i].cnt*a[i].len);
}
return ans;
}
int main()
{
scanf("%s", s+1);
Init_Tr();
Insert();
printf("%lld\n", solve());
return 0;
}