/**
 * Author: Sergey Kopeliovich (Burunduk30@gmail.com)
 */

#include <bits/stdc++.h>

using namespace std;

#define forn(i, n) for (int i = 0; i < (int)(n); i++)

const int N = 1e5, VN = 2 * N;

char s[N + 1];
map<char,int> t[VN];
int l[VN], r[VN], p[VN]; // ребро p[v] -> v это отрезок [l[v], r[v]) исходной строки
int cc, n, suf[VN], vn = 2, v = 1, pos; // идём по ребру из p[v] в v, сейчас стоим в pos

void go( int v ) {
	int no = cc++;
	for (auto p : t[v]) {
		v = p.second;
		printf("%d %d %d\n", no, l[v], min(n, r[v]));
		go(v);
	}
}

int main() {
	assert(freopen("suftree.in", "r", stdin));
	assert(freopen("suftree.out", "w", stdout));

	gets(s);
	forn(i, 127) t[0][i] = 1; // 0 = фиктивная, 1 = корень
	l[1] = -1;
	for (n = 0; s[n]; n++) {
		char c = s[n];
		auto new_leaf = [&]( int v ) {
			p[vn] = v, l[vn] = n, r[vn] = N, t[v][c] = vn++;
		};
		go:;
		if (r[v] <= pos) { 
			if (!t[v].count(c)) {
				new_leaf(v), v = suf[v], pos = r[v];
				goto go;
			}
			v = t[v][c], pos = l[v] + 1;
		} else if (c == s[pos]) {
			pos++;
		} else {
			int x = vn++;
			l[x] = l[v], r[x] = pos, l[v] = pos;
			p[x] = p[v], p[v] = x;
			t[p[x]][s[l[x]]] = x, t[x][s[pos]] = v;
			new_leaf(x);
			v = suf[p[x]], pos = l[x];
			while (pos < r[x])
				v = t[v][s[pos]], pos += r[v] - l[v];
			suf[x] = (pos == r[x] ? v : vn);
			pos = r[v] - (pos - r[x]);
			goto go;
		}
	}
	printf("%d\n", vn - 1);
	go(1);
}