Problem:
Score : 500 points

Problem Statement

Snuke has a string s.
From this string, Anuke, Bnuke, and Cnuke obtained strings a, b, and c, respectively, as follows:

Choose a non-empty (contiguous) substring of s (possibly s itself). Then, replace some characters (possibly all or none) in it with ?s.

For example, if s is mississippi, we can choose the substring ssissip and replace its 1-st and 3-rd characters with ? to obtain ?s?ssip.

You are given the strings a, b, and c.
Find the minimum possible length of s.

Constraints

1 \leq |a|, |b|, |c| \leq 2000

a, b, and c consists of lowercase English letters and ?s.

Input

Input is given from Standard Input in the following format:

a
b
c

Output

Print the minimum possible length of s.

Sample Input 1

a?c
der
cod

Sample Output 1

7

For example, s could be atcoder.

Sample Input 2

atcoder
atcoder
???????

Sample Output 2

7

a, b, and c may not be distinct.

Solution in C# (Mono 4.6.2.0) (judge verdict: Accepted)

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace AtCorder
{
	public class Program
	{
		static void Main(string[] args)
		{
			var cin = new Scanner2();
			string a = cin.Next();
			string b = cin.Next();
			string c = cin.Next();

			int an = a.Length;
			int bn = b.Length;
			int cn = c.Length;
			int MAXL = 2000;

			int jmax = 20000;
			int joffset = jmax / 2;
			bool[] ab = new bool[jmax];
			bool[] ac = new bool[jmax];
			bool[] bc = new bool[jmax];
			for (int i = MAXL * -2; i <= MAXL * 2; i++) {
				ab[i + joffset] = Check(a, an, 0, b, bn, i);
			}

			for (int i = MAXL * -2; i <= MAXL * 2; i++) {
				ac[i + joffset] = Check(a, an, 0, c, cn, i);
			}

			for (int i = MAXL * -2; i <= MAXL * 2; i++) {
				bc[i + joffset] = Check(b, bn, 0, c, cn, i);
			}

			int ans = an + bn + cn;
			for (int i = MAXL * -2; i <= MAXL * 2; i++) {
				for (int j = MAXL * -2; j <= MAXL * 2; j++) {
					bool abOK = ab[i + joffset];
					bool acOK = ac[j + joffset];
					bool bcOK = bc[j - i + joffset];
					if (abOK && acOK && bcOK) {
						int min = Math.Min(0, Math.Min(i, j));
						int max = Math.Max(an, Math.Max(i + bn, j + cn));
						int l = max - min;
						ans = Math.Min(ans, l);
					}
				}
			}

			Console.WriteLine(ans);
		}

		static bool Check(string x, int xn, int xi, string y, int yn, int yi)
		{
			if (xi < yi) {
				int diff = yi - xi;
				int num = Math.Min(xn - diff, yn);
				for (int i = 0; i < num; i++) {
					if (Compare(x[i + diff], y[i]) == false) {
						return false;
					}
				}
			} else {
				int diff = xi - yi;
				int num = Math.Min(yn - diff, xn);
				for (int i = 0; i < num; i++) {
					if (Compare(x[i], y[i + diff]) == false) {
						return false;
					}
				}
			}

			return true;
		}

		static bool Compare(char x, char y)
		{
			if (x == '?' || y == '?') {
				return true;
			} else {
				return x == y;
			}
		}
	}

	public static class Helper
	{
		public static void UpdateMin32<T>(T array, int index, int value) where T : IList<int>
			=> array[index] = Math.Min(array[index], value);
		public static void UpdateMax32<T>(T array, int index, int value) where T : IList<int>
			=> array[index] = Math.Max(array[index], value);
		public static void UpdateMin64<T>(T array, int index, long value) where T : IList<long>
			=> array[index] = Math.Min(array[index], value);
		public static void UpdateMax64<T>(T array, int index, long value) where T : IList<long>
			=> array[index] = Math.Max(array[index], value);

		public static T[] Array<T>(int n, Func<int, T> init)
			=> Enumerable.Range(0, n).Select(x => init(x)).ToArray();
		public static List<T> List<T>(int n, Func<int, T> init)
			=> Enumerable.Range(0, n).Select(x => init(x)).ToList();
	}

	public class Scanner2
	{
		private readonly char[] delimiter_ = new char[] { ' ' };
		private readonly string filePath_;
		private readonly Func<string> reader_;
		private string[] buf_;
		private int index_;

		public Scanner2(string file = "")
		{
			if (string.IsNullOrWhiteSpace(file)) {
				reader_ = Console.ReadLine;
			} else {
				filePath_ = file;
				var fs = new StreamReader(file);
				reader_ = fs.ReadLine;
			}
			buf_ = new string[0];
			index_ = 0;
		}

		public string Next()
		{
			if (index_ < buf_.Length) {
				return buf_[index_++];
			}

			string st = reader_();
			while (st == "") {
				st = reader_();
			}

			buf_ = st.Split(delimiter_, StringSplitOptions.RemoveEmptyEntries);
			if (buf_.Length == 0) {
				return Next();
			}

			index_ = 0;
			return buf_[index_++];
		}

		public int Int() => int.Parse(Next());
		public long Long() => long.Parse(Next());
		public double Double() => double.Parse(Next());

		public int[] ArrayInt(int N, int add = 0)
		{
			int[] Array = new int[N];
			for (int i = 0; i < N; i++) {
				Array[i] = Int() + add;
			}
			return Array;
		}

		public long[] ArrayLong(int N, long add = 0)
		{
			long[] Array = new long[N];
			for (int i = 0; i < N; i++) {
				Array[i] = Long() + add;
			}
			return Array;
		}

		public double[] ArrayDouble(int N, double add = 0)
		{
			double[] Array = new double[N];
			for (int i = 0; i < N; i++) {
				Array[i] = Double() + add;
			}
			return Array;
		}

		public void Save(string text)
		{
			if (string.IsNullOrWhiteSpace(filePath_)) {
				return;
			}

			File.WriteAllText(filePath_ + "_output.txt", text);
		}
	}
}