#include using namespace std; typedef pair ii; typedef vector vi; typedef long long ll; struct segment_tree { int n; vector values; segment_tree(int _n) : n(_n), values(_n, 0) { } void diagonal_update(int l, int r, int a, ll b) { for(int i = l; i <= r; ++i) { values[i] += b; b += a; } } ll query(int l, int r) { ll ans = 0; for(int i = l; i <= r; ++i) { ans += values[i]; } return ans; } void count_char(vector &cnt, string &s) { for(int i = 0; i < n; ++i) { cnt[s[i]] += values[i]; } } }; struct segment_tree2 { int n; vector values; segment_tree2(int _n) : n(_n), values(_n, 0) { } void range_incr(int l, int r) { for(int i = l; i <= r; ++i) values[i]++; } void collect(vector &cnt, int l, int r) { for(int i = l; i <= r; ++i) { if(values[i] != 0) { for(char c : to_string(values[i])) cnt[c - '0']++; } values[i] = 0; } } }; 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); for(int i = 1; i < s.size(); ++i) { numer.collect(res, lcp[i - 1], s.size() - 1); numer.range_incr(0, s.size() - sfa[i] - 1); } numer.collect(res, 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]); 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); alpha.diagonal_update(sfa[i] + lcp[i - 1], s.size() - 1, -1, prnt); } res = vector(256, 0); alpha.count_char(res, s); for(int i = 0; i < 256; ++i) { if(res[i] == 0) continue; cout << ((char) i) << ' '; cout << res[i] << '\n'; } }