#include using namespace std; typedef pair ii; typedef vector vi; typedef long long ll; struct node { ll x, a, b; }; struct node2 { ll x, inc; bool fix; node2() : x(0), inc(0), fix(true) { } void incr(ll y) { if(fix) x += y; else inc += y; } }; struct segment_tree { int n; vector nodes; segment_tree(int _n) : n(_n), nodes(2 * _n) { } void diagonal_update(int l, int r, int a, int b, int v, int tl, int tr) { if(l > r) return; if(l == tl && r == tr) { nodes[v].a += a; nodes[v].b += b; } else { int tm = (tl + tr) / 2; int ind = 2 * (tm - tl + 1); diagonal_update(l, min(r, tm), a, b, v + 1, tl, tm); ll incr = ((ll) a) * max(0, tm + 1 - l); diagonal_update(max(l, tm + 1), r, a, b + incr, v + ind, tm + 1, tr); } } ll query(int l, int r, int v, int tl, int tr) { if(l > r) return 0; propagate(v, tl, tr); if(l == tl && r == tr) return nodes[v].x; int tm = (tl + tr) / 2; int ind = 2 * (tm - tl + 1); return query(l, min(r, tm), v + 1, tl, tm) + query(max(l, tm + 1), r, v + ind, tm + 1, tr); } void propagate(int v, int tl, int tr) { if(tl < tr) { int tm = (tl + tr) / 2; int ind = 2 * (tm - tl + 1); nodes[v + 1].a += nodes[v].a; nodes[v + 1].b += nodes[v].b; nodes[v + ind].a += nodes[v].a; nodes[v + ind].b += nodes[v].b; nodes[v + ind].b += (tm - tl + 1) * nodes[v].a; } nodes[v].x += nodes[v].b; nodes[v].a = 0; nodes[v].b = 0; } void count_char(vector &cnt, string &s, int v, int tl, int tr) { propagate(v, tl, tr); if(tl == tr) { cnt[s[tl]] += nodes[v].x; } else { int tm = (tl + tr) / 2; int ind = 2 * (tm - tl + 1); count_char(cnt, s, v + 1, tl, tm); count_char(cnt, s, v + ind, tm + 1, tr); } } }; struct segment_tree2 { int n; vector nodes; segment_tree2(int _n) : n(_n), nodes(2 * _n) { } void range_incr(int l, int r, int v, int tl, int tr) { if(l > r) return; if(l == tl && r == tr) { nodes[v].incr(1); } else { propagate(v, tl, tr); int tm = (tl + tr) / 2; int ind = 2 * (tm - tl + 1); range_incr(l, min(r, tm), v + 1, tl, tm); range_incr(max(l, tm + 1), r, v + ind, tm + 1, tr); consolidate(v, tl, tr); } } void collect(vector &cnt, int l, int r, int v, int tl, int tr) { if(l > r) return; if(l == tl && r == tr && nodes[v].fix) { if(nodes[v].x != 0) for(char c : to_string(nodes[v].x)) cnt[c - '0'] += tr - tl + 1; nodes[v].x = 0; } else { propagate(v, tl, tr); int tm = (tl + tr) / 2; int ind = 2 * (tm - tl + 1); collect(cnt, l, min(r, tm), v + 1, tl, tm); collect(cnt, max(l, tm + 1), r, v + ind, tm + 1, tr); consolidate(v, tl, tr); } } void propagate(int v, int tl, int tr) { if(tl < tr) { int tm = (tl + tr) / 2; int ind = 2 * (tm - tl + 1); if(nodes[v].fix) { nodes[v + 1].x = nodes[v].x; nodes[v + ind].x = nodes[v].x; nodes[v + 1].fix = true; nodes[v + ind].fix = true; } else { nodes[v + 1].incr(nodes[v].inc); nodes[v + ind].incr(nodes[v].inc); nodes[v].inc = 0; } } } void consolidate(int v, int tl, int tr) { if(tl < tr) { int tm = (tl + tr) / 2; int ind = 2 * (tm - tl + 1); if(nodes[v + 1].fix && nodes[v + ind].fix && nodes[v + 1].x == nodes[v + ind].x) { nodes[v].fix = true; nodes[v].x = nodes[v + 1].x; } else { nodes[v].fix = false; } } } }; vector sort_cyclic_shifts(string const& s) { int n = s.size(); const int alphabet = 256; vector p(n), c(n), cnt(max(alphabet, n), 0); for (int i = 0; i < n; i++) cnt[s[i]]++; for (int i = 1; i < alphabet; i++) cnt[i] += cnt[i-1]; for (int i = 0; i < n; i++) p[--cnt[s[i]]] = i; c[p[0]] = 0; int classes = 1; for (int i = 1; i < n; i++) { if (s[p[i]] != s[p[i-1]]) classes++; c[p[i]] = classes - 1; } vector pn(n), cn(n); for (int h = 0; (1 << h) < n; ++h) { for (int i = 0; i < n; i++) { pn[i] = p[i] - (1 << h); if (pn[i] < 0) pn[i] += n; } fill(cnt.begin(), cnt.begin() + classes, 0); for (int i = 0; i < n; i++) cnt[c[pn[i]]]++; for (int i = 1; i < classes; i++) cnt[i] += cnt[i-1]; for (int i = n-1; i >= 0; i--) p[--cnt[c[pn[i]]]] = pn[i]; cn[p[0]] = 0; classes = 1; for (int i = 1; i < n; i++) { pair cur = {c[p[i]], c[(p[i] + (1 << h)) % n]}; pair prev = {c[p[i-1]], c[(p[i-1] + (1 << h)) % n]}; if (cur != prev) ++classes; cn[p[i]] = classes - 1; } c.swap(cn); } return p; } vector suffix_array_construction(string s) { s += "$"; vector sorted_shifts = sort_cyclic_shifts(s); sorted_shifts.erase(sorted_shifts.begin()); return sorted_shifts; } vector lcp_construction(string const& s, vector const& p) { int n = s.size(); vector rank(n, 0); for (int i = 0; i < n; i++) rank[p[i]] = i; int k = 0; vector lcp(n-1, 0); for (int i = 0; i < n; i++) { if (rank[i] == n - 1) { k = 0; continue; } int j = p[rank[i] + 1]; while (i + k < n && j + k < n && s[i+k] == s[j+k]) k++; lcp[rank[i]] = k; if (k) k--; } return lcp; } int main() { ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); string s; cin >> s; vi sfa = suffix_array_construction(s); vi lcp = lcp_construction(s, sfa); segment_tree2 numer(s.size()); vector res(10, 0); numer.range_incr(0, s.size() - sfa[0] - 1, 1, 0, s.size() - 1); for(int i = 1; i < s.size(); ++i) { numer.collect(res, lcp[i - 1], s.size() - 1, 1, 0, s.size() - 1); numer.range_incr(0, s.size() - sfa[i] - 1, 1, 0, s.size() - 1); } numer.collect(res, 0, s.size() - 1, 1, 0, s.size() - 1); for(int i = 0; i < 10; ++i) { if(res[i] == 0) continue; cout << ((char) (i + '0')) << ' '; cout << res[i] << '\n'; } segment_tree alpha(s.size()); alpha.diagonal_update(sfa[0], s.size() - 1, -1, s.size() - sfa[0], 1, 0, s.size() - 1); for(int i = 1; i < s.size(); ++i) { int prnt = s.size() - sfa[i] - lcp[i - 1]; alpha.diagonal_update(sfa[i], sfa[i] + lcp[i - 1] - 1, 0, prnt, 1, 0, s.size() - 1); alpha.diagonal_update(sfa[i] + lcp[i - 1], s.size() - 1, -1, prnt, 1, 0, s.size() - 1); } res = vector(256, 0); alpha.count_char(res, s, 1, 0, s.size() - 1); for(int i = 0; i < 256; ++i) { if(res[i] == 0) continue; cout << ((char) i) << ' '; cout << res[i] << '\n'; } }