unittest
{
	assert( [ "a?c", "der", "cod" ].parse.expand.solve == 7 );
	assert( [ "atcoder", "atcoder", "???????" ].parse.expand.solve == 7 );
	
	assert( [ "tc", "atcoder", "de" ].parse.expand.solve == 7 );		// 追加ケース
	assert( [ "tc?", "atcoder", "?de" ].parse.expand.solve == 7 );		// 追加ケース
	assert( [ "tc?", "atcoder", "cde" ].parse.expand.solve == 10 );		// 追加ケース
	assert( [ "?".repeat( 2000 ).join( "" ), "?".repeat( 2000 ).join( "" ), "?".repeat( 2000 ).join( "" ) ].parse.expand.solve == 2000 );		// 追加ケース
}



import std.algorithm;
import std.conv;
import std.range;
import std.stdio;
import std.typecons;

void main()
{
	stdin.byLineCopy.parse.expand.solve.writeln;
}

auto parse( Range )( Range input )
if( isInputRange!Range && is( ElementType!Range == string ) )
{
	auto abc = new dchar[][ 3 ];
	foreach( ref x; abc )
	{
		x = input.front.to!( dchar[] );
		input.popFront;
	}
	return tuple( abc );
}

auto solve( dchar[][] abc )
{
	// msss[ 文字列X ][ 文字列Y ][ i文字目 ] = 文字列Xのi文字目以降と文字列Yを比較して不一致か
	auto msss = new bool[][][]( 3, 3, reduce!( ( x, y ) => x + y.length )( 1L, abc ) );
	foreach( i, ref mss; msss )
	{
		foreach( j, ref ms; mss )
		{
			if( i == j ) continue;
			foreach( k, ref m; ms )
			{
				if( abc[ i ].length <= k ) break;
				foreach( x, y; lockstep( abc[ i ][ k .. min( $, k + abc[ j ].length ) ], abc[ j ] ) )
				{
					if( x != '?' && y != '?' && x != y )
					{
						m = true;
						break;
					}
				}
			}
		}
	}
	
	// 文字列の順番を入れ替えながら全列挙
	auto ans = long.max;
	foreach( ps; iota( 3 ).permutations )
	{
		foreach( i; 0L .. abc[ ps[ 0 ] ].length + 1 )
		{
			foreach( j; 0L .. max( abc[ ps[ 0 ] ].length.signed - i, abc[ ps[ 1 ] ].length ) + 1 )
			{
				if( !msss[ ps[ 0 ] ][ ps[ 1 ] ][ i ] && !msss[ ps[ 1 ] ][ ps[ 2 ] ][ j ] && !msss[ ps[ 0 ] ][ ps[ 2 ] ][ i + j ] )
				{
					auto a = max( abc[ ps[ 0 ] ].length, i + abc[ ps[ 1 ] ].length, i + j + abc[ ps[ 2 ] ].length );
					ans = min( ans, a );
				}
			}
		}
	}
	return ans;
}
