1. 程式人生 > >【迴文樹 && 求每個迴文串出現次數】HYSBZ

【迴文樹 && 求每個迴文串出現次數】HYSBZ

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; }