#include<bits/stdc++.h>usingnamespacestd;structPalindromeTree{structNode{intlen,link;intnext[26];Node():len(0),link(0){memset(next,0,sizeof(next));}};strings;vector<Node>tree;intlast,sz;PalindromeTree(){tree.resize(2);tree[0].len=-1;tree[0].link=0;// node 0: gốc lẻ (len=-1 để "aba" mở rộng đều 2 đầu)tree[1].len=0;tree[1].link=0;// node 1: gốc chẵn rỗng (len=0)last=1;sz=2;// last: palindrome hậu tố dài nhất của tiền tố đã xử lý}voidextend(intpos){intcur=last;// cur: bắt đầu từ palindrome hậu tố dài nhấtintc=s[pos]-'a';// c: ký tự hiện tại (dạng chỉ số)// Tìm palindrome P sao cho s[pos-1-|P|] == s[pos] (mở rộng được thêm 1 cặp)while(true){intcurlen=tree[cur].len;// curlen: độ dài palindrome đang thử// pos-1-curlen: vị trí đối xứng bên trái; >= 0 để không tràn mảngif(pos-1-curlen>=0&&s[pos-1-curlen]==s[pos])break;cur=tree[cur].link;// không mở rộng được → lùi về suffix link ngắn hơn}if(tree[cur].next[c]){// palindrome mới đã tồn tại → không tạo nodelast=tree[cur].next[c];return;}last=sz++;// last mới: node palindrome vừa tạotree.push_back(Node());tree[last].len=tree[cur].len+2;// thêm 1 cặp ký tự 2 đầu → dài thêm 2tree[cur].next[c]=last;// gắn cạnh c từ P đến palindrome mớiif(tree[last].len==1){// palindrome 1 ký tự → link về gốc rỗngtree[last].link=1;return;}// Tìm suffix link: palindrome đối xứng dài nhất là hậu tố đúngcur=tree[cur].link;while(true){intcurlen=tree[cur].len;if(pos-1-curlen>=0&&s[pos-1-curlen]==s[pos])break;cur=tree[cur].link;}tree[last].link=tree[cur].next[c];}intcountDistinct(){returnsz-2;// trừ 2 node gốc}};intmain(){strings;cin>>s;PalindromeTreept;pt.s=s;for(inti=0;i<(int)s.size();i++){pt.extend(i);}cout<<pt.countDistinct()<<"\n";return0;}
// SAI: chỉ tạo 1 gốc → palindrome lẻ đầu tiên không mở rộng đượctree.resize(1);tree[0].len=0;tree[0].link=0;last=0;// ĐÚNG: luôn 2 gốc — node 0 (len=-1) cho lẻ, node 1 (len=0) cho chẵntree.resize(2);tree[0].len=-1;tree[0].link=0;tree[1].len=0;tree[1].link=0;last=1;sz=2;// Đáp án = sz - 2 (trừ 2 gốc) — quên trừ là sai +2.
// SAI: cho palindrome len=1 đi tìm link bằng vòng while chung → có thể link về chính nó// ĐÚNG: len == 1 luôn link về node 1 (rỗng), return ngayif(tree[last].len==1){tree[last].link=1;return;}
// SAI: bỏ kiểm tra biên → pos-1-curlen = -1 khi palindrome dài chạm đầu xâuif(s[pos-1-curlen]==s[pos])break;// ĐÚNG: luôn kiểm tra >= 0 trước khi so ký tựif(pos-1-curlen>=0&&s[pos-1-curlen]==s[pos])break;