#include <bits/stdc++.h>
using namespace std;
int n, i, j, b[1000005], can_be_1[2], fa[1000005];
int fnd(int x) {
  if (fa[x] == x) return x;
  return fa[x] = fnd(fa[x]);
}
int get(int x) {
  x = fnd(x);
  while (x && !b[x]) {
    fa[x] = fnd(x - 1);
    x = fa[x];
  }
  return x;
}
int ans, sa[1000005], sb[1000005], sc[1000005];
int stk[1000005], tp;
string st;
struct tri {
  int a, b, c;
};
tri a[1000005];
int len;
struct seg {
  int mx, mi, mxs;
} tr[4000005];
seg operator+(seg x, seg y) {
  seg z;
  z.mi = min(x.mi, y.mi);
  z.mx = max(x.mx, y.mx);
  if (x.mx == z.mx)
    z.mxs = x.mxs;
  else
    z.mxs = y.mxs;
  return z;
}
int tag[1000005];
void pushup(int x) { tr[x] = tr[x + x] + tr[x + x + 1]; }
void upd(int x, int y) {
  tr[x].mx += y;
  tr[x].mi += y;
  tag[x] += y;
}
void pushdo(int x) {
  if (tag[x]) {
    upd(x + x, tag[x]);
    upd(x + x + 1, tag[x]);
    tag[x] = 0;
  }
}
void build(int x, int l, int r) {
  if (l == r) {
    tr[x] = (seg){sa[l], sb[l], l};
    return;
  }
  int mid = (l + r) / 2;
  build(x + x, l, mid);
  build(x + x + 1, mid + 1, r);
  pushup(x);
}
void update(int x, int l, int r, int ql, int qr, int c) {
  if (ql <= l && r <= qr) {
    upd(x, c);
    return;
  }
  int mid = (l + r) / 2;
  pushdo(x);
  if (ql <= mid) update(x + x, l, mid, ql, qr, c);
  if (qr > mid) update(x + x + 1, mid + 1, r, ql, qr, c);
  pushup(x);
}
seg query(int x, int l, int r, int ql, int qr) {
  if (ql <= l && r <= qr) {
    return tr[x];
  }
  int mid = (l + r) / 2;
  pushdo(x);
  if (qr <= mid) return query(x + x, l, mid, ql, qr);
  if (ql > mid) return query(x + x + 1, mid + 1, r, ql, qr);
  return query(x + x, l, mid, ql, qr) + query(x + x + 1, mid + 1, r, ql, qr);
}
void print1(int x, int l, int r) {
  if (l == r) {
    cerr << tr[x].mx << ' ';
    return;
  }
  int mid = (l + r) / 2;
  pushdo(x);
  print1(x + x, l, mid);
  print1(x + x + 1, mid + 1, r);
}
void print2(int x, int l, int r) {
  if (l == r) {
    cerr << tr[x].mi << ' ';
    return;
  }
  int mid = (l + r) / 2;
  pushdo(x);
  print2(x + x, l, mid);
  print2(x + x + 1, mid + 1, r);
}
int main() {
  can_be_1[0] = can_be_1[1] = 1;
  cin >> st;
  n = st.size();
  a[++len] = (tri){0, 0, 0};
  int ca = 0, cb = 0, cc = 0, cd = 0;
  for ((i) = 1; (i) <= (n); (i)++) {
    if (cd && st[i - 1] != '?') {
      b[len] = cd;
      cd = 0;
    }
    if (st[i - 1] == '0') {
      can_be_1[(i & 1) ^ 1] = 0;
      ca = max(ca, ++cc);
    }
    if (st[i - 1] == '1') {
      can_be_1[(i & 1)] = 0;
      cb = min(cb, --cc);
    }
    if (st[i - 1] == '?') {
      if (!cd) {
        a[++len] = (tri){ca, cb, cc};
        ca = cb = cc = 0;
      }
      cd++;
    }
  }
  if (cd) b[len] = cd;
  a[++len] = (tri){ca, cb, cc};
  n = len;
  for ((i) = 1; (i) <= (n); (i)++) {
    sa[i] = sc[i - 1] + b[i - 1] + a[i].a;
    sb[i] = sc[i - 1] + b[i - 1] + a[i].b;
    sc[i] = sc[i - 1] + a[i].c + b[i - 1];
  }
  build(1, 1, n);
  ans = tr[1].mx - tr[1].mi;
  int lst = 0;
  for ((i) = 1; (i) <= (n); (i)++) fa[i] = i;
  for (;;) {
    seg tmp = tr[1];
    int pos = get(tmp.mxs - 1);
    if (tmp.mx == 0 || pos <= 1) break;
    seg tl = query(1, 1, n, 1, pos);
    int tt = min((tmp.mx - tl.mx + 1) / 2, b[pos]);
    b[pos] -= tt;
    assert(tt);
    update(1, 1, n, pos + 1, n, -(tt - 1) * 2);
    ans = min(ans, tr[1].mx - tr[1].mi);
    update(1, 1, n, pos + 1, n, -2);
    ans = min(ans, tr[1].mx - tr[1].mi);
    if (tmp.mx - tt * 2 < tl.mx) {
      if (!lst)
        lst = pos + 1;
      else {
        update(1, 1, n, lst, n, 1);
        ans = min(ans, tr[1].mx - tr[1].mi);
        lst = pos + 1;
      }
    }
  }
  if (lst) {
    update(1, 1, n, lst, n, 1);
    ans = min(ans, tr[1].mx - tr[1].mi);
  }
  if (ans == 0) ans = 1;
  if (ans == 1 && !can_be_1[0] && !can_be_1[1]) ans = 2;
  cout << ans << endl;
  return 0;
}
