// #define _GLIBCXX_DEBUG 1
#include <bits/stdc++.h>
using namespace std;

struct Node;
typedef Node * PNode;
struct Node {
	int lo, hi;
	PNode par, suf;
	map <char, PNode> next;

	Node (int lo_, int hi_, PNode par_) :
	    lo (lo_), hi (hi_), par (par_), suf (nullptr) {}
};

void solve (string & s) {
	auto n = int (s.size ());
	int const oo = n;
	PNode root = new Node (0, 0, nullptr);
	int nv = 1;

	PNode v = root;
	int p = 0;
	// v->lo <= p <= v->hi
	// [v->lo .. v->hi)
	for (int i = 0; i < n; i++) {
		PNode prev = nullptr;
		while (true) {
			if (p < v->hi) {
				if (s[i] == s[p]) {
					p += 1;
					break;
				} else {
					auto w = new Node (v->lo, p, v->par);
					nv += 1;
					// w is son of v->par:
					v->par->next[s[v->lo]] = w;
					v->lo = p;
					// v is son of w:
					v->par = w;
					w->next[s[p]] = v;
					v = w;
				}
			}
			// suflink
			if (prev != nullptr) {
				prev->suf = v;
			}
			prev = v;
			if (v->next.contains(s[i])) {
				v = v->next[s[i]];
				p = v->lo + 1;
				break;
			} else {
				auto u = new Node (i, oo, v);
				nv += 1;
				v->next[s[i]] = u;
				// go to next suffix
				if (v == root) break;
				auto dist = p - v->lo;
				v = v->par;
				if (v == root) dist -= 1; else v = v->suf;
				p = v->lo; // p must always be between v->lo and v->hi
				while (dist > 0) {
					v = v->next[s[i - dist]];
					p = v->lo;
					auto sub = min (dist, v->hi - v->lo);
					dist -= sub;
					p += sub;
				}
			}
		}
	}

	int now = 0;
	function <void (PNode, int)> recur = [&] (PNode v, int p) {
		auto id = now++;
		if (id) cout << p << " " << v->lo << " " << v->hi << "\n";
		for (auto [_, u] : v->next) {
			recur (u, id);
		}
	};

	cout << nv << endl;
	recur (root, 0);
}

int main () {
	ios_base::sync_with_stdio (false);
	cin.tie (nullptr);
	string s;
	while (cin >> s) {
		solve (s);
	}
}
