#include using namespace std; using int64 = long long; //const int mod = 1e9 + 7; const int mod = 998244353; const int64 infll = (1LL << 62) - 1; const int inf = (1 << 30) - 1; struct IoSetup { IoSetup() { cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(10); cerr << fixed << setprecision(10); } } iosetup; template< typename T1, typename T2 > ostream &operator<<(ostream &os, const pair< T1, T2 > &p) { os << p.first << " " << p.second; return os; } template< typename T1, typename T2 > istream &operator>>(istream &is, pair< T1, T2 > &p) { is >> p.first >> p.second; return is; } template< typename T > ostream &operator<<(ostream &os, const vector< T > &v) { for(int i = 0; i < (int) v.size(); i++) { os << v[i] << (i + 1 != v.size() ? " " : ""); } return os; } template< typename T > istream &operator>>(istream &is, vector< T > &v) { for(T &in: v) is >> in; return is; } template< typename T1, typename T2 > inline bool chmax(T1 &a, T2 b) { return a < b && (a = b, true); } template< typename T1, typename T2 > inline bool chmin(T1 &a, T2 b) { return a > b && (a = b, true); } template< typename T = int64 > vector< T > make_v(size_t a) { return vector< T >(a); } template< typename T, typename... Ts > auto make_v(size_t a, Ts... ts) { return vector< decltype(make_v< T >(ts...)) >(a, make_v< T >(ts...)); } template< typename T, typename V > typename enable_if< is_class< T >::value == 0 >::type fill_v(T &t, const V &v) { t = v; } template< typename T, typename V > typename enable_if< is_class< T >::value != 0 >::type fill_v(T &t, const V &v) { for(auto &e: t) fill_v(e, v); } template< typename F > struct FixPoint : F { FixPoint(F &&f) : F(forward< F >(f)) {} template< typename... Args > decltype(auto) operator()(Args &&... args) const { return F::operator()(*this, forward< Args >(args)...); } }; template< typename F > inline decltype(auto) MFP(F &&f) { return FixPoint< F >{forward< F >(f)}; } /** * @brief Palindromic Tree(回文木) * @see https://math314.hateblo.jp/entry/2016/12/19/005919 * @docs docs/palindromic-tree.md */ template< typename T = char > struct PalindromicTree { public: struct Node { map< T, int > link; // 子のidx int suffix_link; // 最長回文接尾辞のidx int len; // 対応する回文の長さ vector< int > idx; // 対応する回文を最長回文接尾辞とするidx int delta_link; Node() = default; Node(int suf, int len) : suffix_link(suf), len(len), delta_link(-1) {} }; vector< Node > ns; int ptr; vector< T > vs; private: int find_prev_palindrome(int cur) const { int pos = (int) vs.size() - 1; for(;;) { int rev = pos - 1 - ns[cur].len; if(rev >= 0 and vs[rev] == vs.back()) break; cur = ns[cur].suffix_link; } return cur; } bool output_dfs(int v, int id, vector< T > &ret) const { if(v == id) return true; for(auto &nxt: ns[v].link) { if(output_dfs(nxt.second, id, ret)) { ret.emplace_back(nxt.first); return true; } } return false; } public: PalindromicTree() : ptr(0) { ns.emplace_back(0, -1); // 長さ -1 ns.emplace_back(0, 0); // 長さ 0 } PalindromicTree(const string &S) : PalindromicTree() { add(S); } int delta(int t) const { if(ns[t].suffix_link <= 0) return -1; return ns[t].len - ns[ns[t].suffix_link].len; } int add(const T &x) { int idx = (int) vs.size(); vs.emplace_back(x); int cur = find_prev_palindrome(ptr); auto res = ns[cur].link.insert(make_pair(x, (int) ns.size())); ptr = res.first->second; if(res.second) { ns.emplace_back(-1, ns[cur].len + 2); if(ns.back().len == 1) { ns.back().suffix_link = 1; } else { ns.back().suffix_link = ns[find_prev_palindrome(ns[cur].suffix_link)].link[x]; } if(delta(ptr) == delta(ns.back().suffix_link)) { ns.back().delta_link = ns[ns.back().suffix_link].delta_link; } else { ns.back().delta_link = ns.back().suffix_link; } } ns[ptr].idx.emplace_back(idx); return ptr; } void add(const string &s) { for(auto &x: s) add(x); } vector< int > build_frequency() const { vector< int > ret(ns.size()); for(int i = (int) ns.size() - 1; i > 0; i--) { ret[i] += (int) ns[i].idx.size(); ret[ns[i].suffix_link] += ret[i]; } return ret; } vector< T > output(int idx) const { if(idx == 0) return {-1}; if(idx == 1) return {0}; vector< T > ret; output_dfs(0, idx, ret); output_dfs(1, idx, ret); int start = (int) ret.size() - 1; if(ns[idx].len & 1) --start; for(int i = start; i >= 0; i--) { ret.emplace_back(ret[i]); } return ret; } int size() const { return (int) ns.size(); } Node &operator[](int idx) { return ns[idx]; } }; int main() { string S; cin >> S; int N = (int) S.size(); vector< int64 > dp1(N + 1), dp2(N + 1), buf(N + 3); PalindromicTree t; for(int i = 0; i < N; i++) { int id = t.add(S[i]); if(t[id].len == i + 1) { dp1[i + 1] = 1; } while(t[id].len > 0) { buf[id] = dp1[i + 1 - t[t[id].delta_link].len - t.delta(id)]; if(t[id].suffix_link != t[id].delta_link) { buf[id] += buf[t[id].suffix_link]; } dp2[i + 1] += buf[id]; id = t[id].delta_link; } } PalindromicTree ts; int64 ret = 0, sum = 0; for(int i = N - 1; i >= 0; i--) { ret += 1LL * dp2[i] * sum; int id = ts.add(S[i]); if(ts[id].len == N - i) { sum++; } } cout << ret << "\n"; }