#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <map>
#include <unordered_set>
#include <iostream>
#include <random>
#include <queue>
#include <ctime>
#include <cassert>
#include <cassert>
#include <numeric>
#include <complex>
#include <string>
#include <cstring>
#include <chrono>
#include <random>
#include <bitset>
#include <stack>
#include <fstream>
#include <array>
#include <iomanip>
#include <functional>
#include <memory>
#include <ctime>
using namespace std;
//using int = long long;
using ld = long double;
#define debug(x) std::cerr << __FUNCTION__ << ":" << __LINE__ << " " << #x << " = " << x << '\n';
const int INF = 1e9;
vector<int> z_func(string &s){
vector<int> z(s.size());
int l = -1;
int r = -1;
for (int i = 1; i < s.size(); i++){
if (i <= r){
z[i] = min(r - i + 1, z[i - l]);
}
while (i + z[i] < s.size() && s[z[i]] == s[i + z[i]]){
z[i]++;
}
if (i + z[i] - 1 > r){
r = i + z[i] - 1;
l = i;
}
}
return z;
}
struct segtree{
int size;
vector<int> tree;
void init(int n){
size = 1;
while (size <= n) {
size *= 2;
}
tree.resize(2 * size - 1, -INF);
}
void build(vector<int> &ar, int x, int lx, int rx){
if (rx - lx == 1){
if (lx < ar.size()){
tree[x] = ar[lx];
}
return;
}
build(ar,2*x+1,lx,(lx+rx)/2);
build(ar,2*x+2,(lx+rx)/2,rx);
tree[x]=max(tree[2*x+1],tree[2*x+2]);
}
void build(vector<int> &ar){
init(ar.size());
build(ar,0,0,size);
}
int get(int l, int r, int x, int lx, int rx){
if (r <= lx || rx <= l){
return -INF;
}
if (lx >= l && rx <= r){
return tree[x];
}
return max(get(l,r,2*x+1,lx,(lx+rx)/2),get(l,r,2*x+2,(lx+rx)/2,rx));
}
int get(int l, int r){
r++;
return get(l,r ,0,0,size);
}
};
void solve(){
string s;
cin >> s;
string s_rev = s;
reverse(s_rev.begin(), s_rev.end());
string s2 = s_rev + "#" + s;
vector<int> z = z_func(s2);
vector<int> temp(s.size());
reverse(z.begin(), z.end());
for (int i =0; i <= s_rev.size(); i++){
z.pop_back();
}
reverse(z.begin(), z.end());
vector<int> checker;
int n = s.size();
vector<int> fst(n + 1, -1);
for (int i = 1; i <= n; i++){
checker.push_back(i);
}
reverse(checker.begin(), checker.end());
for (int i = 0; i < z.size(); i++){
while (checker.size() && z[i] >= checker.back()){
fst[checker.back()] = i;
checker.pop_back();
}
}
vector<int> d1(s.size());
int l = -1;
int r = -1;
for (int i =0; i < s.size(); i++){
if(i <= r){
d1[i] = min(r - i + 1, d1[(l + r) / 2 - (i - (l + r) / 2)]);
}
while (i + d1[i] < n && i - d1[i] >= 0 && s[i + d1[i]] == s[i - d1[i]]){
d1[i]++;
}
if (i + d1[i] -1 > r){
r = i + d1[i] - 1;
l = i - d1[i] + 1;
}
}
fst[0]=0;
segtree tree_odd;
tree_odd.build(d1);
int ans = 0;
int k;
int tk = 0;
for (int i =0; 2 * i < n; i++){
if (fst[i]==-1){
break;
}
int len_ost = n - 1 - fst[i] + 1;
len_ost = len_ost - 2 * i;
if (len_ost <= 0){
continue;
}
int left = fst[i] + i;
int right = n - 1 - i;
int largest_even =0 ;
int largest_odd = 0;
l = 0;
r = right - left + 1;
int m;
l = 0;
r = ((right -left + 1) + 1) / 2;
while (l < r){
m = (l + r + 1) / 2;
int dd = 2 * m - 1;
if (2 * tree_odd.get(left + dd / 2, right - dd / 2) - 1 >= dd){
l = m;
}
else{
r = m - 1;
}
}
largest_odd = 2*l-1;
if (2 * i + max(largest_odd, largest_even) > ans){
ans = 2 * i + max(largest_odd, largest_even);
tk = i;
}
}
if (tk == 0){
cout << 1 << '\n';
for (int i = 0; i < n; i++){
if (2 * d1[i] - 1 == ans){
cout << (i - d1[i] + 1) + 1 << " " << ans << '\n';
return;
}
}
}
cout << 3 << '\n';
int need_p = ans - 2 * tk;
cout << fst[tk] + 1 << " " << tk << '\n';
for (int i = 0; i < n; i++){
if (2 * d1[i] - 1 >= need_p && (i - need_p / 2) > fst[tk] + tk - 1){
cout << (i - need_p / 2) + 1 << " " << need_p << '\n';
break;
}
}
cout << n - tk + 1 << " " << tk << '\n';
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
t=1;
//cin >> t;
while (t--) {
solve();
}
return 0;
}