#include "bits/stdc++.h"

using namespace std;

using u32 = uint32_t;
using u64 = uint64_t;
using i64 = int64_t;

#define FOR(i,a,b) for (int i = (a); i < (b); i++)
#define F0R(i,b) FOR(i,0,b)
#define RFO(i,a,b) for (int i = (b-1); i >= a; i--)
#define RF0(i,b) RFO(i,0,b)
#define F first
#define S second
#define ADD(a,b) (((u64)a + b)%MOD)
#define MUL(a,b) (((u64)a * b)%MOD)

constexpr int MOD = 998244353;

template<class T>
constexpr const T& clamp(const T& v, const T& lo, const T& hi) {
  assert(!(hi < lo));
  return (v < lo) ? lo : (hi < v) ? hi : v;
}


// a + b
vector<string> fusion(string a, string b, int* minLength = nullptr) {

  //#define FUSION_DEBUG_OUTPUT

  // 現在確認されている長さの最小値以上であれば処理しない
  if (minLength != nullptr) {
    if (max(a.size(), b.size()) >= *minLength) {
      return {};
    }
  }

  // a + b から b + a になるまで1文字ずつずらして照合していく

  vector<string> results;

  // b を置く位置 (a座標系)
  FOR(i, -((int)b.size()), (int)a.size() + 1) {

#ifdef FUSION_DEBUG_OUTPUT
    cout << i << ' ';
#endif

    bool match = true;

    // 重なっている範囲 (a座標系)
    int l = max<int>(0, i);
    int r = min<int>(a.size(), i + b.size());

    // 現在確認されている長さの最小値以上であれば処理しない
    if (minLength != nullptr) {
      if (a.size() + b.size() - (r - l) >= *minLength) {
        if (i > 0) {
          return {};
        }
        continue;
      }
    }

    // b座標系
    int bl = max<int>(-i, 0);

    F0R(j, r - l) {
#ifdef FUSION_DEBUG_OUTPUT
      //if (i == 13) {
      //  cout << a[l + j] << ',' << b[bl + j] << ' ';
      //}
#endif

      if (a[l + j] != '?' && b[bl + j] != '?' && a[l + j] != b[bl + j]) {
        match = false;
        break;
      }
    }

    // この配置で a と b は結合できる
    if (match) {

      if (minLength != nullptr) {
        *minLength = a.size() + b.size() - (r - l);
      }
      else {
        // 結合

        string s;

        // 左
        if (i > 0) {
          s += a.substr(0, i);
        }
        else {
          s += b.substr(0, -i);
        }

#ifdef FUSION_DEBUG_OUTPUT
        cout << '"' << s << '"';
#endif

        // 中
        F0R(j, r - l) {
          s += a[l + j] != '?' ? a[l + j] : b[bl + j] != '?' ? b[bl + j] : '?';
        }

#ifdef FUSION_DEBUG_OUTPUT
        cout << '"' << s << '"';
#endif

        // 右
        if (i + b.size() < a.size()) {
          s += a.substr(r);
        }
        else {
          s += b.substr(bl + r - l);
        }
#ifdef FUSION_DEBUG_OUTPUT
        cout << '"' << s << '"' << '\n';
#endif
        results.push_back(s);
      }
    }
  }

#ifdef FUSION_DEBUG_OUTPUT
  cout << "End of fusion()\n";
#endif

  return move(results);
}


bool cmpLength(const string& a, const string& b) {
  return a.size() < b.size();
}


int getMin(string a, string b, string c) {

  //#define GETMIN_DEBUG_OUTPUT

  int minLength = INT32_MAX;

  {
    vector<string> strs = move(fusion(a, b));
    sort(strs.begin(), strs.end(), cmpLength);
    for (auto& s : strs) {
      fusion(s, c, &minLength);
    }
  }
  {
    vector<string> strs = move(fusion(a, c));
    sort(strs.begin(), strs.end(), cmpLength);
    for (auto& s : strs) {
      fusion(s, b, &minLength);
    }
  }

  return minLength;
}

/*
【失敗した方法】
a, b, c からまず2つを選び結合する。その文字列と最後の一つを結合する。
結合とは、左と右から少しずつ重なる部分を増やしていって、
左右それぞれ"最初"に得られた最小の文字列を得ること。
しかしこの方法では正しい結果にならないものがあった。以下がそれ。
aacad? d???a?aca?adda?ddc ?cd?ba

これは20文字のacdbbaaaacadaddacddcからとった部分文字列だが、これを
上記アルゴリズムで計算すると最小22文字になる。

かなりレアケースだが、どの2つの組み合わせをとっても
長さを最小にできるいくつかのパターンのうち"中央より"のものを選ばなければ
最適な結果が得られない場合、上記方法ではNGとなる。

【失敗した方法2】
なので1回目の結合では、あり得る全てのパターンを取得する必要がある。
それらのパターンと、最後1つの文字列とを結合し、最小を求めればよい。と思う。

だがダメだった。例えば 1234567890 という文字列を 123 7890 34567 と切り取った場合、
左から順に結合していくだけでは、 123???7890という文字列は得られない。
よって考え直す必要がある。
最初に結合する文字列の組み合わせを複数試せば大丈夫かもしれない。
けどこの方法は既に処理が遅くいくつかTLEも出ているので、速度改善ができないと使えない。。
*/

int main() {
  cin.tie(0);
  ios_base::sync_with_stdio(false);

#if 1
  string a, b, c;
  cin >> a >> b >> c;

  cout << getMin(a, b, c);
#elif 0
  // fusion test
  while (true) {
    string a, b;
    cin >> a >> b;
    cout << a << " + " << b << '\n';
    for (auto s : fusion(a, b, INT32_MAX)) {
      cout << " = " << s << '\n';
    }
  }
  return 0;
#else
  // automatic testing
  constexpr int Length = 4000;
  constexpr double wildcardRate = 0.99;

  random_device seed_gen;
  mt19937 engine(seed_gen());
  uniform_int_distribution<int> dist('a', 'd');
  uniform_int_distribution<int> distLen(0, Length - 1);
  uniform_real_distribution<> realDist(0, 1);

  while (true) {

    string s;
    F0R(i, Length) {
      s += (char)dist(engine);
    }
    //cout << s << '\n';

    string ss[3];
    F0R(i, 3) {
      int a = distLen(engine), b = distLen(engine);
      if (a > b) swap(a, b);
      ss[i] = s.substr(a, b - a + 1);
      F0R(j, ss[i].size()) {
        if (realDist(engine) < wildcardRate) {
          ss[i][j] = '?';
        }
      }
      cout << ss[i] << ' ';
    }
    cout << '\n';
    return 0;

    int result = getMin(ss[0], ss[1], ss[2]);

    if (result > s.size()) {
      cout << "** Mismatch **\n";
      cout << s << '\n';
      F0R(i, 3) {
        cout << ss[i] << ' ';
}
      cout << "\nsize=" << s.size() << " result=" << result << "\n";
      return 0;
    }
  }

#endif
}


Measured memory_bytes: 17152