// Find all palindrome substrings in a given string
// (C)2012 mgr Jerzy Wałaszek
//-----------------------------
#include <iostream>
#include <string>
#include <cstdlib>
#include <time.h>
#include <vector>
#include <algorithm>
//#include <string>
//#include <time.h>
using namespace std;
#define MAX 100000
#define mod 1000000007
struct suffix
{
int index;
int rank[2];
};
int cmp(struct suffix a, struct suffix b)
{
return (a.rank[0] == b.rank[0])? (a.rank[1] < b.rank[1] ?1: 0):
(a.rank[0] < b.rank[0] ?1: 0);
}
vector<int>suffixArr;
void buildSuffixArray(string txt, int n)
{
struct suffix suffixes[n];
for (int i = 0; i < n; i++)
{
suffixes[i].index = i;
suffixes[i].rank[0] = txt[i] - 'a';
suffixes[i].rank[1] = ((i+1) < n)? (txt[i + 1] - 'a'): -1;
}
sort(suffixes, suffixes+n, cmp);
int ind[n];
for (int k = 4; k < 2*n; k = k*2)
{
int rank = 0;
int prev_rank = suffixes[0].rank[0];
suffixes[0].rank[0] = rank;
ind[suffixes[0].index] = 0;
for (int i = 1; i < n; i++)
{
if (suffixes[i].rank[0] == prev_rank &&
suffixes[i].rank[1] == suffixes[i-1].rank[1])
{
prev_rank = suffixes[i].rank[0];
suffixes[i].rank[0] = rank;
}
else
{
prev_rank = suffixes[i].rank[0];
suffixes[i].rank[0] = ++rank;
}
ind[suffixes[i].index] = i;
}
for (int i = 0; i < n; i++)
{
int nextindex = suffixes[i].index + k/2;
suffixes[i].rank[1] = (nextindex < n)?
suffixes[ind[nextindex]].rank[0]: -1;
}
sort(suffixes, suffixes+n, cmp);
}
for (int i = 0; i < n; i++)
suffixArr.push_back(suffixes[i].index);
}
vector<pair<int,long long>> palins[MAX+5];
// Checks for proper border
bool isSuffix (int jStart, pair<int,size_t> it_i)
{
for (auto& it_j : palins[jStart])
{
if((int)it_i.second == (int)it_j.second )
return true;
}
return false;
}
// Checks for palindromes and then for proper borders
int isPalin(string &str, int i, int j)
{
int count = 0;
if (str[i] == str[j])
{
count++;
count %= mod;
}
for (auto& it : palins[i])
{
if ( j - i > it.first && str[i] == str[j])
{
if (isSuffix( j - it.first, it ))
{
count++;
count %= mod;
}
}
}
return count;
}
// Finds all palindromes for the string
void get_palins(string &s)
{
int N = s.length();
int i, j, k, // iterators
rp, // length of 'palindrome radius'
R[2][N+1]; // table for storing results (2 rows for odd- and even-length palindromes
s = "@" + s + "#"; // insert 'guards' to iterate easily over s
for(j = 0; j <= 1; j++)
{
R[j][0] = rp = 0; i = 1;
while(i <= N)
{
while(s[i - rp - 1] == s[i + j + rp]) rp++;
R[j][i] = rp;
k = 1;
while((R[j][i - k] != rp - k) && (k < rp))
{
R[j][i + k] = min(R[j][i - k],rp - k);
k++;
}
rp = max(rp - k,0);
i += k;
}
}
s = s.substr(1,N); // remove 'guards'
for(i = 1; i <= N; i++)
{
for(j = 0; j <= 1; j++)
for(rp = R[j][i]; rp > 0; rp--)
{
int begin = i - rp - 1;
int end_count = 2 * rp + j;
int end = begin + end_count - 1;
if (!(begin == 0 && end == N -1 ))
{
long long hsh = hash<string>{}(s.substr(begin, end_count));
palins[begin].push_back({end_count - 1, hsh });
}
}
}
}
int main()
{
string s;
cin >> s;
int start_s = clock();
buildSuffixArray(s, s.length());
int n = suffixArr.size();
int count = 0;
get_palins(s);
// Loops through the suffix array to find all substrings
for(int i = 0;i < n;i++) {
int begin_suffix = suffixArr[i];
int end_suffix = n - suffixArr[i];
for (int j = 2; j <= end_suffix; j++)
{
int left = begin_suffix;
int right = begin_suffix + j - 1;
count += isPalin(s, left, right);
}
}
cout << count << endl;
for (int i = 0; i < MAX+5; i++)
palins[i].clear();
suffixArr.clear();
int stop_s = clock();
float run_time = (stop_s - start_s) / double(CLOCKS_PER_SEC);
cout << endl << "palindromic borders \t\tExecution time = " << run_time << " seconds" << endl;
return 0;
}