題目鏈接:https://www.luogu.org/problem/P5496
題意:給定字符串,求每個位置爲結尾的迴文串個數
思路:裸題,不過注意,是last指針在fail樹上跳,並不是直接輸出num數組
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
typedef long double ld;
const int maxn = 5e5 + 100;
const int mod = 1e9 + 7;
const int inf = 1e9;
char str[maxn];
ll fail[maxn],len[maxn],last,ch[maxn][26],n,num[maxn],cnt[maxn],tot,pos[maxn];
//len[i], 以i結尾的最長迴文子串的長度
//cnt[i]:以i結尾的最長迴文子串相同的子串的個數 在count後得到全部
//num[i] 表示以i結尾的迴文串的種類數
//str[] 存放添加的字符
//fail[] 失配後跳轉到的不等於自身的最長後綴迴文子串。
//pos[] 表示當前最長迴文子串的結束位置
//ch[][] 類似於字典樹,指向當前字符串在兩端同時加上一個字符
//last 最新添加的迴文節點
// tot 總的節點個數
// n表示添加的字符個數
ll newnode(ll x)
{
for(ll i = 0; i < 26; i++) //for 非多次調用迴文樹可省略
ch[tot][i] = 0;
cnt[tot] = 0;
num[tot] = 0;
len[tot] = x;
return tot++;
}
void init()
{
tot = 0;
last = 0;
newnode(0); //偶串根節點
newnode(-1); //奇串根節點
str[0] = '#'; //0號位置設置成一個不會出現的字符
fail[0] = 1; //兩個根節點互指
fail[1] = 0;
return ;
}
ll get_fail(ll p,ll x)
{
while(str[ x-len[p]-1 ] != str[x] )
p = fail[p];
return p;
}
void count()
{
for(ll i = tot-1; i >= 0; i--)
cnt[ fail[i] ] += cnt[i];
//父親累加兒子的cnt,因爲如果fail[v]=u,則u一定是v的子迴文串!
return ;
}
void build() //構建迴文樹
{
ll k = 0;
for(ll i = 1; i <= n; i++)
{
if(i >= 2)
str[i] = (str[i]-97+k)%26+97;
ll tmp = get_fail(last,i);
ll s = str[i]-'a';
if(!ch[tmp][s])
{
ll now = newnode(len[tmp]+2);
fail[now] = ch[ get_fail( fail[tmp],i ) ][ s ];
ch[tmp][s] = now;
num[now] = num[fail[now]]+1;
}
last = ch[tmp][s];
pos[last] = i;
cnt[last]++;
k = num[last];
cout << k << " ";
}
return ;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin >> str+1;
n = strlen(str+1);
init();
build();
//count();
return 0;
}