#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include //#include //#include //#include //#include //#include //#include //#include //#include //#if __cplusplus >= 201103L //#include //#include //#include //#include // //#define cauto const auto& //#else //#endif using namespace std; typedef long long ll; typedef pair pii; typedef pair pll; typedef vector vint; typedef vector > vvint; typedef vector vll, vLL; typedef vector > vvll, vvLL; #define VV(T) vector > template void initvv(vector > &v, int a, int b, const T &t = T()){ v.assign(a, vector(b, t)); } template void convert(const F &f, T &t){ stringstream ss; ss << f; ss >> t; } #undef _P #define _P(...) (void)printf(__VA_ARGS__) #define reep(i,a,b) for(int i=(a);i<(b);++i) #define rep(i,n) reep((i),0,(n)) #define ALL(v) (v).begin(),(v).end() #define PB push_back #define F first #define S second #define mkp make_pair #define RALL(v) (v).rbegin(),(v).rend() #define DEBUG #ifdef DEBUG #define dump(x) cout << #x << " = " << (x) << endl; #define debug(x) cout << #x << " = " << (x) << " (L" << __LINE__ << ")" << " " << __FILE__ << endl; #else #define dump(x) #define debug(x) #endif #define MOD 1000000007LL #define EPS 1e-8 #define INF 0x3f3f3f3f #define INFL 0x3f3f3f3f3f3f3f3fLL #define maxs(x,y) x=max(x,y) #define mins(x,y) x=min(x,y) class charSA{ unsigned char mask[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; #define tget(i) ( (t[(i)/8]&mask[(i)%8]) ? 1 : 0 ) #define tset(i, b) t[(i)/8]=(b) ? (mask[(i)%8]|t[(i)/8]) : ((~mask[(i)%8])&t[(i)/8]) #define chr(i) (cs==sizeof(int)?((unsigned int*)s)[i]:((unsigned char *)s)[i]) #define isLMS(i) (i>0 && tget(i) && !tget(i-1)) // find the start or end of each bucket void getBuckets(unsigned char *s, int *bkt, int n, int K, int cs, bool end) { int i, sum = 0; for (i = 0; i <= K; i++) bkt[i] = 0; // clear all buckets for (i = 0; i < n; i++) bkt[chr(i)]++; // compute the size of each bucket for (i = 0; i <= K; i++) { sum += bkt[i]; bkt[i] = end ? sum : sum - bkt[i]; } } // compute SAl void induceSAl(unsigned int *t, int *SA, unsigned char *s, int *bkt, int n, int K, int cs, bool end) { int i, j; getBuckets(s, bkt, n, K, cs, end); // find starts of buckets for (i = 0; i < n; i++) { j = SA[i] - 1; if (j >= 0 && !tget(j)) SA[bkt[chr(j)]++] = j; } } // compute SAs void induceSAs(unsigned int *t, int *SA, unsigned char *s, int *bkt, int n, int K, int cs, bool end) { int i, j; getBuckets(s, bkt, n, K, cs, end); // find ends of buckets for (i = n - 1; i >= 0; i--) { j = SA[i] - 1; if (j >= 0 && tget(j)) SA[--bkt[chr(j)]] = j; } } void SA_IS(unsigned char *s, int *SA, int n, int K, int cs) { int i, j; unsigned int *t = new unsigned int[n / 8 + 1]; // LS-type array in bits assert(t!=NULL); tset(n-2, 0); tset(n-1, 1); // the sentinel must be in s1, important!!! for (i = n - 3; i >= 0; i--) tset(i, (chr(i) 0 && (isLMS(pos+d) || isLMS(prev+d))) break; if (diff) { name++; prev = pos; } pos = (pos % 2 == 0) ? pos / 2 : (pos - 1) / 2; SA[n1 + pos] = name - 1; } for (i = n - 1, j = n - 1; i >= n1; i--) if (SA[i] >= 0) SA[j--] = SA[i]; // stage 2: solve the reduced problem // recurse if names are not yet unique int *SA1 = SA, *s1 = SA + n - n1; if (name < n1){ SA_IS((unsigned char*) s1, SA1, n1, name - 1, sizeof(int)); } else{ // generate the suffix array of s1 directly for (i = 0; i < n1; i++) assert(s1[i]>=0),SA1[s1[i]] = i; } getBuckets(s, bkt, n, K, cs, true); // find ends of buckets for (i = 1, j = 0; i < n; i++) if (isLMS(i)) s1[j++] = i; // get p1 for (i = 0; i < n1; i++) SA1[i] = s1[SA1[i]]; // get index in s for (i = n1; i < n; i++) SA[i] = -1; // init SA[n1..n-1] for (i = n1 - 1; i >= 0; i--) { j = SA[i]; SA[i] = -1; SA[--bkt[chr(j)]] = j; } induceSAl(t, SA, s, bkt, n, K, cs, false); induceSAs(t, SA, s, bkt, n, K, cs, true); delete[] bkt; delete[] t; } public: charSA(){ } vector buildCharSA(string &u, int K, int cs = 1){ int n = u.size(); int *sa = new int[n+1]; // integer sequence unsigned char* s = (unsigned char *) u.c_str(); SA_IS(s, sa, n + 1, K, cs); vector ret(n); for(int i = 0; i < n; i++){ ret[i]=sa[i+1]; } delete[] sa; return ret; } }; ll lcp[1000010]; void calc_lcp(vector sa, string s) { int n=s.size(),k=0; vector rank(n,0); for(int i=0; i> x) << x); } static inline INT intlog2(INT n){ return(__builtin_clz(n)^31); } static inline INT lsbset (INT n){ return(__builtin_ctz(n)); } struct rmqinfo * rm_query_preprocess(VAL * array, INT alen){ struct rmqinfo * info; INT i, j, g, rows, cols, block_cnt, rowelmlen; INT *block_min, **sparse, *labels; INT gstack[32], gstacksize = 0; info = new rmqinfo; block_cnt = ((alen-1) >> 5) + 1; block_min = new INT[block_cnt]; for(i = j = 0; i < alen; i++){ if(i % 32 == 0){ if(i > 0) j++; block_min[j] = i; } else if(VAL_LT(array[i],array[block_min[j]])){ block_min[j] = i; } } rows = intlog2(block_cnt); sparse = NULL; if(rows > 0){ sparse = new INT*[rows]; sparse[0] = new INT[block_cnt - 1]; for(i = 0; i < block_cnt - 1; i++){ if(VAL_LT(array[block_min[i+1]],array[block_min[i]])) sparse[0][i] = block_min[i+1]; else sparse[0][i] = block_min[i]; } for(j = 1; j < rows; j++){ rowelmlen = 2 << j; /* 2^{j+1} */ cols = block_cnt - rowelmlen + 1; sparse[j] = new INT[cols]; for(i = 0; i < cols; i++){ if(VAL_LT(array[sparse[j-1][i + (rowelmlen >> 1)]],array[sparse[j-1][i]])) sparse[j][i] = sparse[j-1][i + (rowelmlen >> 1)]; else sparse[j][i] = sparse[j-1][i]; } } } labels = new INT[alen]; for(i = 0; i < alen; i++){ if(i % 32 == 0) gstacksize = 0; labels[i] = 0; while(gstacksize > 0 && (VAL_LT(array[i],array[gstack[gstacksize-1]]))){ gstacksize--; } if(gstacksize > 0){ g = gstack[gstacksize-1]; labels[i] = labels[g] | (1 << (g%32)); } gstack[gstacksize++] = i; } info->array = array; info->sparse = sparse; info->block_min = block_min; info->labels = labels; info->alen = alen; return info; } INT rm_query(const struct rmqinfo * info, INT l, INT r){ INT blocknum_l, blocknum_r, blockdiff, blockmin; INT tmp, v, bpos; INT v1, v2, pos1, pos2; if(l == r) return l; if(l > r){ tmp = l; l = r; r = tmp; } blocknum_l = (l >> 5); blocknum_r = (r >> 5); /* obtain which blocks l and r will come in */ bpos = blocknum_l << 5; switch(blockdiff = blocknum_r - blocknum_l){ case 0: v = clearbits(info->labels[r], l % 32); /* clear (x - 1) insiginificant bits */ return ((v == 0) ? r : bpos + lsbset(v)); break; case 1: tmp = bpos + 31; v1 = clearbits(info->labels[tmp], l%32); v2 = info->labels[r]; pos1 = (v1 == 0) ? tmp : (bpos + lsbset(v1)); pos2 = (v2 == 0) ? r : lsbset(v2) + (blocknum_r<<5); return((VAL_LT(info->array[pos2],info->array[pos1])) ? pos2 : pos1); break; default: tmp = bpos + 31; v1 = clearbits(info->labels[tmp], l%32); v2 = info->labels[r]; pos1 = (v1 == 0) ? tmp : (bpos + lsbset(v1)); pos2 = (v2 == 0) ? r : lsbset(v2) + (blocknum_r<<5); if(blockdiff == 2){ blockmin = info->block_min[blocknum_l+1]; } else { int t1, t2, k; k = intlog2(blockdiff-1) - 1; t1 = info->sparse[k][blocknum_l+1]; t2 = info->sparse[k][blocknum_r - (1 << (k+1))]; blockmin = (VAL_LT(info->array[t2],info->array[t1])) ? t2 : t1; } pos1 = (VAL_LT(info->array[blockmin],info->array[pos1])) ? blockmin : pos1; return((VAL_LT(info->array[pos2],info->array[pos1])) ? pos2 : pos1); } } rmqinfo *rmq; string s; int n; // RMQ rmq; vint Rank; int LCE(int l, int r){ return lcp[rm_query(rmq, min(Rank[l], Rank[r])+1, max(Rank[l], Rank[r]))]; // return rmq.query(min(Rank[l], Rank[r])+1, max(Rank[l], Rank[r])); } static const int MAX_SIZE = 1 << 22; //segment tree のサイズ。 2^17 ≒ 1.3 * 10^5 ll all[2 * MAX_SIZE - 1], part[2 * MAX_SIZE - 1]; // segment tree //区間[a, b)に値xを加算する. void add(int a, int b, ll x, int k = 0, int l = 0, int r = MAX_SIZE) { if (a <= l && r <= b){ //[l, r)が[a, b)に完全に内包されていれば all[k] += x; //[l, r)の全ての区間が持つ値としてxを足す. } else if (l < b && a < r){ //[l, r)と[a, b)が交差していれば part[k] += (min(b, r) - max(a, l)) * x; //交差している分の値を, 部分的な和を持つノードに加算する. add(a, b, x, k * 2 + 1, l, (l + r) / 2); //子でも同じ処理を行う. add(a, b, x, k * 2 + 2, (l + r) / 2, r); //〃. } } ll sum(int a, int b, int k = 0, int l = 0, int r = MAX_SIZE) { if (b <= l || r <= a){ //[a, b)と[l, r)が全く交差しない場合 return (0); } else if (a <= l && r <= b){ //完全に内包されていれば return (all[k] * (r - l) + part[k]); } else { //[l, r)と[a, b)が交差していれば ll res; res = (min(b, r) - max(a, l)) * all[k]; //そのノードの全ての要素が持つ値のうち, [a, b)に属すものの分だけを加算する. res += sum(a, b, k * 2 + 1, l, (l + r) / 2); //子ノードで和を求める. res += sum(a, b, k * 2 + 2, (l + r) / 2, r); //〃 return (res); } } bool isPal(string a){ string b = a; reverse(ALL(b)); return a == b; } int func(string a){ int ret = 0; rep(i,a.size()-1){ if(isPal(a.substr(0,i+1))&&isPal(a.substr(i+1))) { ret++; } } return ret; } vector> se; charSA csa; vint sa; void mainmain(){ cin>>s; n = s.size(); string r = s; reverse(ALL(r)); string sr = s+'$'+r; sa = csa.buildCharSA(sr, 256); calc_lcp(sa, sr); // rep(i,sa.size()){ // cout<=1){ int l = i-le+1; int r = i; auto it = lower_bound(ALL(se), mkp(pii(r, INF), 1)); it--; while(1){ int ll,rr,t; ll=it->F.F,rr=it->F.S,t=it->S; if(rrF.F,rr=it->F.S,t=it->S; if(rr=1){ int l = i-le+1; int r = i; int tt = i+1+le; reep(j,l,r+1){ b[tt-(j-l)]+=b[j]; } } } // odd pal int le = (i==0?0:LCE(i+1, 2*n-(i-1))); int l = i-le; int r = i; int tt = i+le; reep(j,l,r+1){ b[tt-(j-l)]+=b[j]; } } } if(lim) rep(i,n-1){ b[i] = sum(i,i+1); } // rep(i,n){ // cout<=0;i--){ c[i] += c[i+1]; } ll ans = 0; rep(i,n-2){ ans += b[i] * c[i+2]; } cout << ans << endl; // cout<