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 Java8 (OpenJDK 1.8.0) (judge verdict: Wrong Answer)

import java.util.*;
import java.util.stream.Collectors;
import java.io.*;
import java.nio.charset.StandardCharsets;

public class Main {
	String a, b, c;
	int result = 0;

	public static void main(String[] args) throws IOException {
		InputStreamReader reader = new InputStreamReader(System.in, StandardCharsets.UTF_8);
		BufferedReader in = new BufferedReader(reader);
		Main ins = new Main(in);
		ins.calc();
		ins.showResult();
	}

	Main(BufferedReader in) throws IOException {
		a = in.readLine();
		b = in.readLine();
		c = in.readLine();
	}

	boolean isMatch(String a, String b, int indexDiff) {
		// indexチェック
		if (indexDiff >= a.length()) {
			// かぶる場所がないのでOK
			return true;
		}
		boolean result = true;
		for (int i = indexDiff; i < Math.min(a.length(), indexDiff + b.length()); ++i) {
			int bIndex = i - indexDiff;
			if (!isMatch(a.charAt(i), b.charAt(bIndex))) {
				result = false;
				break;
			}
		}
		return result;
	}

	boolean isMatch(char a, char b) {
		if (a == '?' || b == '?') {
			return true;
		} else if (a == b) {
			return true;
		} else {
			return false;
		}
	}

	char merge(char a, char b) {
		if (a == '?' && b == '?') {
			return '?';
		} else if (a == '?') {
			return b;
		} else {
			return a;
		}
	}

	String merge(String a, String b) {
		// bの開始場所を0->a.lengthまで変化させる
		int mergeIndex = 0;
		for (int i = 0; i <= a.length(); ++i) {
			if (isMatch(a, b, i)) {
				mergeIndex = i;
				break;
			}
		}

		StringBuilder strBuilder = new StringBuilder();
		// まず0-mergeIndex未満までaをコピーする
		strBuilder.append(a.substring(0, mergeIndex));
		// 次に，a: mergeindex, b: 0から, a: length, b: length のいずれか小さいほうにマージしていく
		for (int i = mergeIndex; i < Math.min(a.length(), mergeIndex + b.length()); ++i) {
			strBuilder.append(merge(a.charAt(i), b.charAt(i - mergeIndex)));
		}
		if (mergeIndex + b.length() >= a.length()) {
			strBuilder.append(b.substring(b.length() - (mergeIndex + b.length() - a.length()), b.length()));
		} else {
			strBuilder.append(a.substring(mergeIndex + b.length(), a.length()));
		}

		String str = strBuilder.toString();
		return str;
	}

	void calc() {
		result = Integer.MAX_VALUE / 2;
		result = Math.min(result, subCalc(a, b, c).length());
		result = Math.min(result, subCalc(a, c, b).length());
		result = Math.min(result, subCalc(b, a, c).length());
		result = Math.min(result, subCalc(b, c, a).length());
		result = Math.min(result, subCalc(c, a, b).length());
		result = Math.min(result, subCalc(c, b, a).length());
	}

	String subCalc(String latter, String middle, String later) {
		String str = merge(latter, middle);
		String result = merge(str, later);
		// System.out.println(result + ": " + latter + ", " + middle + ", " + later + ": " + str);
		return result;
	}

	void showResult() {
		System.out.println(result);
	}
}