#![allow(bare_trait_objects)]
#![allow(unused_imports)]
#![allow(dead_code)]

use std::fmt::Debug;
use std::io::BufRead;
use std::io::Stdin;
use std::io::StdinLock;
use std::str::FromStr;

pub struct Scanner<'a> {
    // stdin: Stdin,
    buf_read: Box<BufRead + 'a>,
    tokens: Vec<String>,
}

impl<'a> Scanner<'a> {
    pub fn new(buf_read: StdinLock<'a>) -> Self {
        Scanner {
            buf_read: Box::new(buf_read),
            tokens: Vec::new(),
        }
    }

    // pub fn from_stdin() -> Self {
    //     let stdin = Box::leak(Box::new(std::io::stdin()));
    //     Self {
    //         buf_read: Box::new(stdin.lock()),
    //         tokens: Vec::new(),
    //     }
    // }

    pub fn next(&mut self) -> String {
        while self.tokens.is_empty() {
            let mut s = String::new();
            if self.buf_read.read_line(&mut s).is_err() {
                panic!()
            }
            self.tokens = s
                .split_whitespace()
                .filter(|s| !s.is_empty())
                .rev()
                .map(str::to_string)
                .collect();
        }
        self.tokens.pop().unwrap()
    }

    pub fn next_g<T: FromStr>(&mut self) -> T
    where
        T::Err: Debug,
    {
        self.next().parse().unwrap()
    }

    pub fn next_vec_g<T: FromStr>(&mut self, n: usize) -> Vec<T>
    where
        T::Err: Debug,
    {
        (0..n).map(|_| self.next_g()).collect()
    }

    pub fn usize(&mut self) -> usize {
        self.next_g()
    }

    pub fn i64(&mut self) -> i64 {
        self.next_g()
    }

    pub fn vec_i64(&mut self, n: usize) -> Vec<i64> {
        self.next_vec_g(n)
    }
}
fn main() {
    let stdin = std::io::stdin();
    let mut sc = Scanner::new(stdin.lock());

    let a = sc.next();
    let b = sc.next();
    let c = sc.next();

    let mut ans = 10000000;
    let ss = &[a, b, c];
    for i in 0..3 {
        for j in 0..3 {
            for k in 0..3 {
                if i == j || j == k || i == k {
                    continue;
                }

                let a = &ss[i].chars().collect::<Vec<_>>();
                let b = &ss[j].chars().collect::<Vec<_>>();
                let c = &ss[k].chars().collect::<Vec<_>>();

                let f = |a: &[char], b: &[char]| -> Vec<bool> {
                    let mut okij = vec![true; 6001];
                    for i in 0..a.len() {
                        okij[i] = true;
                        for j in 0..b.len() {
                            if i + j < a.len() && a[i + j] != b[j] && a[i + j] != '?' && b[j] != '?'
                            {
                                okij[i] = false;
                                break;
                            }
                        }
                    }
                    okij
                };

                let okij = f(a, b);
                let okik = f(a, c);
                let okjk = f(b, c);

                // eprintln!("{:?}", &okij[0..5]);
                // eprintln!("{:?}", &okik[0..5]);
                // eprintln!("{:?}", &okjk[0..5]);

                for i in 0..a.len() + 1 {
                    for j in 0..std::cmp::max(a.len() - i, b.len()) + 1 {
                        if okij[i] && okik[i + j] && okjk[j] {
                            ans = std::cmp::min(
                                ans,
                                *[a.len(), i + b.len(), i + j + c.len()]
                                    .iter()
                                    .max()
                                    .unwrap(),
                            );
                        }
                    }
                }
            }
        }
    }

    println!("{}", ans);
}


Measured memory_bytes: 4352