#include <bits/stdc++.h>
using namespace std;
using lint = long long int;
using pint = pair<int, int>;
using plint = pair<lint, lint>;
struct fast_ios {
  fast_ios() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(20);
  };
} fast_ios_;
template <typename T>
void ndarray(vector<T> &vec, int len) {
  vec.resize(len);
}
template <typename T, typename... Args>
void ndarray(vector<T> &vec, int len, Args... args) {
  vec.resize(len);
  for (auto &v : vec) ndarray(v, args...);
}
template <typename T>
bool chmax(T &m, const T q) {
  if (m < q) {
    m = q;
    return true;
  } else
    return false;
}
template <typename T>
bool chmin(T &m, const T q) {
  if (m > q) {
    m = q;
    return true;
  } else
    return false;
}
template <typename T1, typename T2>
pair<T1, T2> operator+(const pair<T1, T2> &l, const pair<T1, T2> &r) {
  return make_pair(l.first + r.first, l.second + r.second);
}
template <typename T1, typename T2>
pair<T1, T2> operator-(const pair<T1, T2> &l, const pair<T1, T2> &r) {
  return make_pair(l.first - r.first, l.second - r.second);
}
template <typename T>
istream &operator>>(istream &is, vector<T> &vec) {
  for (auto &v : vec) is >> v;
  return is;
}
template <typename T>
ostream &operator<<(ostream &os, const vector<T> &vec) {
  os << "[";
  for (auto v : vec) os << v << ",";
  os << "]";
  return os;
}
template <typename T>
ostream &operator<<(ostream &os, const deque<T> &vec) {
  os << "deq[";
  for (auto v : vec) os << v << ",";
  os << "]";
  return os;
}
template <typename T>
ostream &operator<<(ostream &os, const set<T> &vec) {
  os << "{";
  for (auto v : vec) os << v << ",";
  os << "}";
  return os;
}
template <typename T>
ostream &operator<<(ostream &os, const unordered_set<T> &vec) {
  os << "{";
  for (auto v : vec) os << v << ",";
  os << "}";
  return os;
}
template <typename T>
ostream &operator<<(ostream &os, const multiset<T> &vec) {
  os << "{";
  for (auto v : vec) os << v << ",";
  os << "}";
  return os;
}
template <typename T>
ostream &operator<<(ostream &os, const unordered_multiset<T> &vec) {
  os << "{";
  for (auto v : vec) os << v << ",";
  os << "}";
  return os;
}
template <typename T1, typename T2>
ostream &operator<<(ostream &os, const pair<T1, T2> &pa) {
  os << "(" << pa.first << "," << pa.second << ")";
  return os;
}
template <typename TK, typename TV>
ostream &operator<<(ostream &os, const map<TK, TV> &mp) {
  os << "{";
  for (auto v : mp) os << v.first << "=>" << v.second << ",";
  os << "}";
  return os;
}
template <typename TK, typename TV>
ostream &operator<<(ostream &os, const unordered_map<TK, TV> &mp) {
  os << "{";
  for (auto v : mp) os << v.first << "=>" << v.second << ",";
  os << "}";
  return os;
}
template <typename _Tp>
struct RangeAddRangeMin {
  int N, head;
  _Tp defaultMin;
  std::vector<_Tp> range_min, range_add;
  static inline _Tp f(_Tp x, _Tp y) noexcept { return std::min(x, y); }
  inline void _merge(int pos) {
    range_min[pos] = f(range_min[pos * 2] + range_add[pos * 2],
                       range_min[pos * 2 + 1] + range_add[pos * 2 + 1]);
  }
  void initialize(const std::vector<_Tp> &data_init, _Tp default_min) {
    N = data_init.size(), head = 1;
    while (head < N) head <<= 1;
    defaultMin = default_min;
    range_min.assign(head * 2, defaultMin);
    range_add.assign(head * 2, _Tp(0, 0));
    std::copy(data_init.begin(), data_init.end(), range_min.begin() + head);
    for (int pos = head; --pos;) _merge(pos);
  }
  RangeAddRangeMin() = default;
  RangeAddRangeMin(const std::vector<_Tp> &data_init, _Tp default_min) {
    initialize(data_init, default_min);
  }
  void _add(int begin, int end, int pos, int l, int r, _Tp vadd) noexcept {
    if (r <= begin or end <= l) return;
    if (begin <= l and r <= end) {
      range_add[pos] = range_add[pos] + vadd;
      return;
    }
    _add(begin, end, pos * 2, l, (l + r) / 2, vadd);
    _add(begin, end, pos * 2 + 1, (l + r) / 2, r, vadd);
    _merge(pos);
  }
  void add(int begin, int end, _Tp vadd) noexcept {
    return _add(begin, end, 1, 0, head, vadd);
  }
  _Tp _get(int begin, int end, int pos, int l, int r) const noexcept {
    if (r <= begin or end <= l) return defaultMin;
    if (begin <= l and r <= end) return range_min[pos] + range_add[pos];
    return f(_get(begin, end, pos * 2, l, (l + r) / 2),
             _get(begin, end, pos * 2 + 1, (l + r) / 2, r)) +
           range_add[pos];
  }
  _Tp get(int begin, int end) const noexcept {
    return _get(begin, end, 1, 0, head);
  }
};
int solve(string S) {
  int N = S.length();
  set<int> frees;
  vector<pint> vp{{0, 0}}, vpinv{{0, 0}};
  {
    int acc = 0;
    for (int i = (0), i_end_ = (N); i < i_end_; i++) {
      if (S[i] == '1')
        acc++;
      else
        acc--;
      if (S[i] == '?') frees.insert(i + 1);
      vp.emplace_back(acc, i + 1);
      vpinv.emplace_back(-acc, -i - 1);
    }
  }
  RangeAddRangeMin<pint> sst(vp, pint(1e9, -1));
  RangeAddRangeMin<pint> sstinv(vpinv, pint(1e9, -1));
  while (true) {
    auto [lv, il] = sstinv.get(0, N + 1);
    lv *= -1, il *= -1;
    auto [rv, ir] = sst.get(0, N + 1);
    bool fin = false;
    if (il >= ir) fin = true;
    auto itr = frees.upper_bound(il);
    if (itr == frees.end() or *itr > ir) fin = true;
    if (fin) return lv - rv;
    int i = *itr;
    S[i - 1] = 'p';
    sst.add(i, N + 1, pint(2, 0));
    sstinv.add(i, N + 1, pint(-2, 0));
    int ev = -sstinv.get(0, N + 1).first - sst.get(0, N + 1).first;
    frees.erase(i);
    auto itr2 = next(itr);
    if (itr2 != frees.end()) {
      int i2 = *itr2;
      sst.add(i, N + 1, pint(-2, 0));
      sstinv.add(i, N + 1, pint(2, 0));
      sst.add(i2, N + 1, pint(2, 0));
      sstinv.add(i2, N + 1, pint(-2, 0));
      int ev2 = -sstinv.get(0, N + 1).first - sst.get(0, N + 1).first;
      if (ev2 < ev) {
        frees.erase(i2);
      } else {
        sst.add(i, N + 1, pint(2, 0));
        sstinv.add(i, N + 1, pint(-2, 0));
        sst.add(i2, N + 1, pint(-2, 0));
        sstinv.add(i2, N + 1, pint(2, 0));
      }
    }
  }
}
int main() {
  string S;
  cin >> S;
  cout << solve(S) << '\n';
}
