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 Rust (1.15.1) (judge verdict: Wrong Answer)

// SNIPPET z
mod z_internal {
    use std::ops::Range;
    pub fn exact_match_len<T: Eq>(s1: &[T], s2: &[T]) -> usize {
        s1.iter().zip(s2).take_while(|&(c1, c2)| c1 == c2).count()
    }
    pub fn update_state<T: Eq>(
        text: &[T], z_box: &mut Range<usize>, z_table: &mut Vec<usize>
    ) -> bool {
        let index = z_table.len();
        if index >= text.len() {
            return false;
        }
        if z_box.end <= index {
            let len = exact_match_len(text, unsafe { text.get_unchecked(index..) });
            *z_box = index..index+len;
            z_table.push(len);
        } else {
            let z_box_right_len = z_box.end - index;
            let prefix_index = index - z_box.start;
            let prefix_right_len = *unsafe { z_table.get_unchecked(prefix_index) };
            if prefix_right_len < z_box_right_len {
                z_table.push(prefix_right_len);
            } else {
                let additional_len = exact_match_len(
                    unsafe { text.get_unchecked(z_box_right_len..) },
                    unsafe { text.get_unchecked(z_box.end..) }
                );
                *z_box = index .. z_box.end + additional_len;
                z_table.push(z_box_right_len + additional_len);
            }
        }
        true
    }
}
pub struct LongestPrefixLengths<'a, T: 'a + Eq> {
    text: &'a [T],
    z_box: std::ops::Range<usize>,
    z_table: Vec<usize>,
}
impl<'a, T: Eq> LongestPrefixLengths<'a, T> {
    fn new(text: &'a [T]) -> LongestPrefixLengths<'a, T> {
        let mut z_table = Vec::with_capacity(text.len());
        z_table.push(0);
        LongestPrefixLengths {
            text: text,
            z_box: 0..0,
            z_table: z_table
        }
    }
}
impl<'a, T: Eq> Iterator for LongestPrefixLengths<'a, T> {
    type Item = usize;
    fn next(&mut self) -> Option<usize> {
        use self::z_internal::*;
        if update_state(self.text, &mut self.z_box, &mut self.z_table) {
            Some(*self.z_table.last().unwrap())
        } else {
            None
        }
    }
}
pub struct ZMatchIndices<T: Eq + Clone> {
    concat: Vec<T>,
    pattern_len: usize,
    z_box: std::ops::Range<usize>,
    z_table: Vec<usize>
}
impl<T: Eq + Clone> Iterator for ZMatchIndices<T> {
    type Item = usize;
    fn next(&mut self) -> Option<usize> {
        use self::z_internal::*;
        while update_state(&self.concat, &mut self.z_box, &mut self.z_table) {
            if *self.z_table.last().unwrap() == self.pattern_len {
                return Some(self.z_table.len() - 1 - (self.pattern_len + 1))
            }
        }
        None
    }
}
impl<T: Eq + Clone> ZMatchIndices<T> {
    fn new<I: Iterator<Item=T>>(pattern: Vec<T>, sep: T, text: I) -> ZMatchIndices<T> {
        use self::z_internal::*;
        let len = pattern.len();
        let mut concat = pattern;
        concat.push(sep);
        concat.extend(text);
        let mut z_box = 0..0;
        let mut z_table = vec![0];
        for _ in 0..len {
            update_state(&concat, &mut z_box, &mut z_table);
        }
        ZMatchIndices {
            concat: concat,
            pattern_len: len,
            z_box: z_box,
            z_table: z_table,
        }
    }
}
pub trait ZString<T: Eq + Clone> {
    fn longest_prefix_lengths(&self) -> LongestPrefixLengths<T>;
    fn z_match_indices(&self, pattern: &Self) -> ZMatchIndices<Option<T>>;
}
impl<T: Eq + Clone> ZString<T> for [T] {
    fn longest_prefix_lengths(&self) -> LongestPrefixLengths<T> {
        LongestPrefixLengths::new(self)
    }
    fn z_match_indices(&self, pattern: &[T]) -> ZMatchIndices<Option<T>> {
        let mut copied = Vec::with_capacity(self.len() + 1 + pattern.len());
        copied.extend(pattern.iter().map(|x| Some(x.clone())));
        ZMatchIndices::new(copied, None, self.iter().map(|x| Some(x.clone())))
    }
}

// SNIPPET read
pub trait Readable {
    type Output;
    fn words_count() -> usize;
    fn read_words(words: &[&str]) -> Result<Self::Output, String>;
}
#[macro_export]
macro_rules! readable {
    ( $t:ty, $words_count:expr, |$words:ident| $read_words:expr ) => {
        impl Readable for $t {
            type Output = $t;
            fn words_count() -> usize { $words_count }
            fn read_words($words: &[&str]) -> Result<$t, String> {
                Ok($read_words)
            }
        }
    };
}
readable!((), 1, |_ss| ());
readable!(String, 1, |ss| ss[0].to_string());
impl Readable for char {
    type Output = char;
    fn words_count() -> usize { 1 }
    fn read_words(words: &[&str]) -> Result<char, String> {
        let chars: Vec<char> = words[0].chars().collect();
        if chars.len() == 1 {
            Ok(chars[0])
        } else {
            Err(format!("cannot parse `{}` as a char", words[0]))
        }
    }
}
pub struct Chars();
impl Readable for Chars {
    type Output = Vec<char>;
    fn words_count() -> usize { 1 }
    fn read_words(words: &[&str]) -> Result<Vec<char>, String> {
        Ok(words[0].chars().collect())
    }
}
impl Readable for i8 {
    type Output = Self;
    fn words_count() -> usize { 1 }
    fn read_words(words: &[&str]) -> Result<i8, String> {
        use std::str::FromStr;
        i8::from_str(words[0]).map_err(|_| {
            format!("cannot parse `{}` as {}", words[0], stringify!(i8))
        })
    }
}
impl Readable for u8 {
    type Output = Self;
    fn words_count() -> usize { 1 }
    fn read_words(words: &[&str]) -> Result<u8, String> {
        use std::str::FromStr;
        u8::from_str(words[0]).map_err(|_| {
            format!("cannot parse `{}` as {}", words[0], stringify!(u8))
        })
    }
}
impl Readable for i16 {
    type Output = Self;
    fn words_count() -> usize { 1 }
    fn read_words(words: &[&str]) -> Result<i16, String> {
        use std::str::FromStr;
        i16::from_str(words[0]).map_err(|_| {
            format!("cannot parse `{}` as {}", words[0], stringify!(i16))
        })
    }
}
impl Readable for u16 {
    type Output = Self;
    fn words_count() -> usize { 1 }
    fn read_words(words: &[&str]) -> Result<u16, String> {
        use std::str::FromStr;
        u16::from_str(words[0]).map_err(|_| {
            format!("cannot parse `{}` as {}", words[0], stringify!(u16))
        })
    }
}
impl Readable for i32 {
    type Output = Self;
    fn words_count() -> usize { 1 }
    fn read_words(words: &[&str]) -> Result<i32, String> {
        use std::str::FromStr;
        i32::from_str(words[0]).map_err(|_| {
            format!("cannot parse `{}` as {}", words[0], stringify!(i32))
        })
    }
}
impl Readable for u32 {
    type Output = Self;
    fn words_count() -> usize { 1 }
    fn read_words(words: &[&str]) -> Result<u32, String> {
        use std::str::FromStr;
        u32::from_str(words[0]).map_err(|_| {
            format!("cannot parse `{}` as {}", words[0], stringify!(u32))
        })
    }
}
impl Readable for i64 {
    type Output = Self;
    fn words_count() -> usize { 1 }
    fn read_words(words: &[&str]) -> Result<i64, String> {
        use std::str::FromStr;
        i64::from_str(words[0]).map_err(|_| {
            format!("cannot parse `{}` as {}", words[0], stringify!(i64))
        })
    }
}
impl Readable for u64 {
    type Output = Self;
    fn words_count() -> usize { 1 }
    fn read_words(words: &[&str]) -> Result<u64, String> {
        use std::str::FromStr;
        u64::from_str(words[0]).map_err(|_| {
            format!("cannot parse `{}` as {}", words[0], stringify!(u64))
        })
    }
}
impl Readable for isize {
    type Output = Self;
    fn words_count() -> usize { 1 }
    fn read_words(words: &[&str]) -> Result<isize, String> {
        use std::str::FromStr;
        <isize>::from_str(words[0]).map_err(|_| {
            format!("cannot parse `{}` as {}", words[0], stringify!(isize))
        })
    }
}
impl Readable for usize {
    type Output = Self;
    fn words_count() -> usize { 1 }
    fn read_words(words: &[&str]) -> Result<usize, String> {
        use std::str::FromStr;
        <usize>::from_str(words[0]).map_err(|_| {
            format!("cannot parse `{}` as {}", words[0], stringify!(usize))
        })
    }
}
impl Readable for f32 {
    type Output = Self;
    fn words_count() -> usize { 1 }
    fn read_words(words: &[&str]) -> Result<f32, String> {
        use std::str::FromStr;
        f32::from_str(words[0]).map_err(|_| {
            format!("cannot parse `{}` as {}", words[0], stringify!(usize))
        })
    }
}
impl Readable for f64 {
    type Output = Self;
    fn words_count() -> usize { 1 }
    fn read_words(words: &[&str]) -> Result<f64, String> {
        use std::str::FromStr;
        f64::from_str(words[0]).map_err(|_| {
            format!("cannot parse `{}` as {}", words[0], stringify!(usize))
        })
    }
}
#[allow(non_camel_case_types)]
pub struct u8_;
impl Readable for u8_ {
    type Output = u8;
    fn words_count() -> usize { 1 }
    fn read_words(words: &[&str]) -> Result<Self::Output, String> {
        u8::read_words(words).map(|n| n-1)
    }
}
#[allow(non_camel_case_types)]
pub struct u16_;
impl Readable for u16_ {
    type Output = u16;
    fn words_count() -> usize { 1 }
    fn read_words(words: &[&str]) -> Result<Self::Output, String> {
        u16::read_words(words).map(|n| n-1)
    }
}
#[allow(non_camel_case_types)]
pub struct u32_;
impl Readable for u32_ {
    type Output = u32;
    fn words_count() -> usize { 1 }
    fn read_words(words: &[&str]) -> Result<Self::Output, String> {
        u32::read_words(words).map(|n| n-1)
    }
}
#[allow(non_camel_case_types)]
pub struct u64_;
impl Readable for u64_ {
    type Output = u64;
    fn words_count() -> usize { 1 }
    fn read_words(words: &[&str]) -> Result<Self::Output, String> {
        u64::read_words(words).map(|n| n-1)
    }
}
#[allow(non_camel_case_types)]
pub struct usize_;
impl Readable for usize_ {
    type Output = usize;
    fn words_count() -> usize { 1 }
    fn read_words(words: &[&str]) -> Result<Self::Output, String> {
        <usize>::read_words(words).map(|n| n-1)
    }
}
impl<T1: Readable, T2: Readable> Readable for (T1, T2) {
    type Output = (T1::Output, T2::Output);
    fn words_count() -> usize {
        T1::words_count() + T2::words_count()
    }
    fn read_words(words: &[&str]) -> Result<Self::Output, String> {
        assert_eq!(words.len(), Self::words_count());
        let mut start = 0;
        let count1 = T1::words_count();
        let val1 = T1::read_words(&words[start .. start+count1])?;
        start += count1;
        let val2 = T2::read_words(&words[start..])?;
        Ok((val1, val2))
    }
}
impl<T1: Readable, T2: Readable, T3: Readable> Readable for (T1, T2, T3) {
    type Output = (T1::Output, T2::Output, T3::Output);
    fn words_count() -> usize {
        T1::words_count() + T2::words_count() + T3::words_count()
    }
    fn read_words(words: &[&str]) -> Result<Self::Output, String> {
        assert_eq!(words.len(), Self::words_count());
        let mut start = 0;
        let count1 = T1::words_count();
        let val1 = T1::read_words(&words[start .. start+count1])?;
        start += count1;
        let count2 = T2::words_count();
        let val2 = T2::read_words(&words[start .. start+count2])?;
        start += count2;
        let val3 = T3::read_words(&words[start..])?;
        Ok((val1, val2, val3))
    }
}
impl<T1: Readable, T2: Readable, T3: Readable, T4: Readable> Readable for (T1, T2, T3, T4) {
    type Output = (T1::Output, T2::Output, T3::Output, T4::Output);
    fn words_count() -> usize {
        T1::words_count() + T2::words_count() + T3::words_count() + T4::words_count()
    }
    fn read_words(words: &[&str]) -> Result<Self::Output, String> {
        assert_eq!(words.len(), Self::words_count());
        let mut start = 0;
        let count1 = T1::words_count();
        let val1 = T1::read_words(&words[start .. start+count1])?;
        start += count1;
        let count2 = T2::words_count();
        let val2 = T2::read_words(&words[start .. start+count2])?;
        start += count2;
        let count3 = T3::words_count();
        let val3 = T3::read_words(&words[start .. start+count3])?;
        start += count3;
        let val4 = T4::read_words(&words[start..])?;
        Ok((val1, val2, val3, val4))
    }
}
impl<T1: Readable, T2: Readable, T3: Readable, T4: Readable, T5: Readable> Readable for (T1, T2, T3, T4, T5) {
    type Output = (T1::Output, T2::Output, T3::Output, T4::Output, T5::Output);
    fn words_count() -> usize {
        T1::words_count() + T2::words_count() + T3::words_count() + T4::words_count() + T5::words_count()
    }
    fn read_words(words: &[&str]) -> Result<Self::Output, String> {
        assert_eq!(words.len(), Self::words_count());
        let mut start = 0;
        let count1 = T1::words_count();
        let val1 = T1::read_words(&words[start .. start+count1])?;
        start += count1;
        let count2 = T2::words_count();
        let val2 = T2::read_words(&words[start .. start+count2])?;
        start += count2;
        let count3 = T3::words_count();
        let val3 = T3::read_words(&words[start .. start+count3])?;
        start += count3;
        let count4 = T4::words_count();
        let val4 = T4::read_words(&words[start .. start+count4])?;
        start += count4;
        let val5 = T5::read_words(&words[start..])?;
        Ok((val1, val2, val3, val4, val5))
    }
}
impl<T1: Readable, T2: Readable, T3: Readable, T4: Readable, T5: Readable, T6: Readable> Readable for (T1, T2, T3, T4, T5, T6) {
    type Output = (T1::Output, T2::Output, T3::Output, T4::Output, T5::Output, T6::Output);
    fn words_count() -> usize {
        T1::words_count() + T2::words_count() + T3::words_count() + T4::words_count() + T5::words_count() + T6::words_count()
    }
    fn read_words(words: &[&str]) -> Result<Self::Output, String> {
        assert_eq!(words.len(), Self::words_count());
        let mut start = 0;
        let count1 = T1::words_count();
        let val1 = T1::read_words(&words[start .. start+count1])?;
        start += count1;
        let count2 = T2::words_count();
        let val2 = T2::read_words(&words[start .. start+count2])?;
        start += count2;
        let count3 = T3::words_count();
        let val3 = T3::read_words(&words[start .. start+count3])?;
        start += count3;
        let count4 = T4::words_count();
        let val4 = T4::read_words(&words[start .. start+count4])?;
        start += count4;
        let count5 = T5::words_count();
        let val5 = T5::read_words(&words[start .. start+count5])?;
        start += count5;
        let val6 = T6::read_words(&words[start..])?;
        Ok((val1, val2, val3, val4, val5, val6))
    }
}
pub trait ReadableFromLine {
    type Output;
    fn read_line(line: &str) -> Result<Self::Output, String>;
}
fn split_into_words(line: &str) -> Vec<&str> {
    #[allow(deprecated)]
    line.trim_right_matches('\n').split_whitespace().collect()
}
impl<T: Readable> ReadableFromLine for T {
    type Output = T::Output;
    fn read_line(line: &str) -> Result<T::Output, String> {
        let words = split_into_words(line);
        if words.len() != T::words_count() {
            return Err(format!("line `{}` has {} words, expected {}",
                               line, words.len(), T::words_count()));
        }
        T::read_words(&words)
    }
}
fn read_words_into_vec<T: Readable>(words: &[&str], line: &str) -> Result<Vec<T::Output>, String> {
    let n = T::words_count();
    assert_eq!(words.len() % n, 0);
    let mut result = Vec::new();
    for chunk in words.chunks(n) {
        match T::read_words(chunk) {
            Ok(v) => result.push(v),
            Err(msg) => {
                let fragment_msg = if n == 1 {
                    format!("word {}", result.len())
                } else {
                    let l = result.len();
                    format!("words {}-{}", n*l + 1, (n+1) * l)
                };
                return Err(format!(
                    "{} of line `{}`: {}", fragment_msg, line, msg
                ));
            }
        }
    }
    Ok(result)
}
fn split_into_words_for_collection<T: Readable>(
    line: &str, prefix_words_count: usize
) -> Result<Vec<&str>, String> {
    let n = T::words_count();
    let words = split_into_words(line);
    if words.len() < prefix_words_count {
        return Err(
            format!("line `{}` has {} words, expected at least {}",
                    line, words.len(), prefix_words_count)
        );
    }
    if (words.len() - prefix_words_count) % T::words_count() != 0 {
        return Err(
            format!("line `{}` has {} words, expected {} + {}n",
                    line, words.len(), prefix_words_count, n)
        );
    }
    Ok(words)
}
#[macro_export]
macro_rules! readable_collection {
    ($u:ident => $collection_in:ty, $collection_out:ty) => {
        readable_collection!($u: [] => $collection_in, $collection_out);
    };
    ($u:ident : [ $( $bound:path ),* ] => $collection_in:ty, $collection_out:ty) => {
        impl<$u: Readable> ReadableFromLine for $collection_in
        where
            <$u as Readable>::Output: Sized $(+ $bound)*
        {
            type Output = $collection_out;
            fn read_line(line: &str) -> Result<Self::Output, String> {
                let words = split_into_words_for_collection::<$u>(line, 0)?;
                Ok(read_words_into_vec::<$u>(&words, line)?.into_iter().collect())
            }
        }
        impl<T1: Readable, $u: Readable> ReadableFromLine for (T1, $collection_in)
        where
            <$u as Readable>::Output: Sized $(+ $bound)*
        {
            type Output = (T1::Output, $collection_out);
            fn read_line(line: &str) -> Result<Self::Output, String> {
                let prefix_len = T1::words_count();
                let words = split_into_words_for_collection::<$u>(line, prefix_len)?;
                let val1 = T1::read_words(&words[..prefix_len])?;
                let rest = read_words_into_vec::<$u>(&words[prefix_len..], line)?;
                Ok((val1, rest.into_iter().collect()))
            }
        }
        impl<T1: Readable, T2: Readable, $u: Readable> ReadableFromLine for (T1, T2, $collection_in)
        where
            <$u as Readable>::Output: Sized $(+ $bound)*
        {
            type Output = (T1::Output, T2::Output, $collection_out);
            fn read_line(line: &str) -> Result<Self::Output, String> {
                let prefix_len = <(T1, T2)>::words_count();
                let words = split_into_words_for_collection::<$u>(line, prefix_len)?;
                let mut start = 0;
                let count1 = T1::words_count();
                let val1 = T1::read_words(&words[start .. start+count1])?;
                start += count1;
                let count2 = T2::words_count();
                let val2 = T2::read_words(&words[start .. start+count2])?;
                let rest = read_words_into_vec::<$u>(&words[prefix_len..], line)?;
                Ok((val1, val2, rest.into_iter().collect()))
            }
        }
        impl<T1: Readable, T2: Readable, T3: Readable, $u: Readable> ReadableFromLine for (T1, T2, T3, $collection_in)
        where
            <$u as Readable>::Output: Sized $(+ $bound)*
        {
            type Output = (T1::Output, T2::Output, T3::Output, $collection_out);
            fn read_line(line: &str) -> Result<Self::Output, String> {
                let prefix_len = <(T1, T2, T3)>::words_count();
                let words = split_into_words_for_collection::<$u>(line, prefix_len)?;
                let mut start = 0;
                let count1 = T1::words_count();
                let val1 = T1::read_words(&words[start .. start+count1])?;
                start += count1;
                let count2 = T2::words_count();
                let val2 = T2::read_words(&words[start .. start+count2])?;
                start += count2;
                let count3 = T3::words_count();
                let val3 = T3::read_words(&words[start .. start+count3])?;
                let rest = read_words_into_vec::<$u>(&words[prefix_len..], line)?;
                Ok((val1, val2, val3, rest.into_iter().collect()))
            }
        }
        impl<T1: Readable, T2: Readable, T3: Readable, T4: Readable, $u: Readable> ReadableFromLine for (T1, T2, T3, T4, $collection_in)
        where
            <$u as Readable>::Output: Sized $(+ $bound)*
        {
            type Output = (T1::Output, T2::Output, T3::Output, T4::Output, $collection_out);
            fn read_line(line: &str) -> Result<Self::Output, String> {
                let prefix_len = <(T1, T2, T3, T4)>::words_count();
                let words = split_into_words_for_collection::<$u>(line, prefix_len)?;
                let mut start = 0;
                let count1 = T1::words_count();
                let val1 = T1::read_words(&words[start .. start+count1])?;
                start += count1;
                let count2 = T2::words_count();
                let val2 = T2::read_words(&words[start .. start+count2])?;
                start += count2;
                let count3 = T3::words_count();
                let val3 = T3::read_words(&words[start .. start+count3])?;
                start += count3;
                let count4 = T4::words_count();
                let val4 = T4::read_words(&words[start .. start+count4])?;
                let rest = read_words_into_vec::<$u>(&words[prefix_len..], line)?;
                Ok((val1, val2, val3, val4, rest.into_iter().collect()))
            }
        }
    };
}
readable_collection!(U => Vec<U>, Vec<U::Output>);
pub fn read<T: ReadableFromLine>() -> T::Output {
    let mut line = String::new();
    std::io::stdin().read_line(&mut line).unwrap();
    T::read_line(&line).unwrap()
}
#[macro_export]
macro_rules! read {
    () => {
        let mut line = String::new();
        std::io::stdin().read_line(&mut line).unwrap();
    };
    ( $pat:pat = $t:ty ) => {
        let $pat = read::<$t>();
    };
    ( $( $pat:pat = $t:ty ),+ ) => {
        read!(($($pat),*) = ($($t),*));
    };
}
#[macro_export]
macro_rules! readls {
    ( $( $pat:pat = $t:ty ),+ ) => {
        $(
            read!($pat = $t);
        )*
    };
}
pub fn readx<T: ReadableFromLine>() -> Vec<T::Output> {
    use std::io::{self, BufRead};
    let stdin = io::stdin();
    let result = stdin.lock().lines().map(|line_result| {
        let line = line_result.expect("read from stdin failed");
        T::read_line(&line).unwrap()
    }).collect();
    result
}
#[macro_export]
macro_rules! readx_loop {
    ( |$pat:pat = $t:ty| $body:expr ) => {
        {
            use std::io::BufRead;
            let stdin = std::io::stdin();
            for line in stdin.lock().lines() {
                let line = line.expect("read from stdin failed");
                let $pat = <$t>::read_line(&line).unwrap();
                $body
            }
        }
    };
    ( |$($pat:pat = $t:ty),*| $body:expr ) => {
        readx_loop!(|($($pat),*) = ($($t),*)| $body);
    };
}
pub fn readn<T: ReadableFromLine>(n: usize) -> Vec<T::Output> {
    use std::io::{self, BufRead};
    let stdin = io::stdin();
    let result: Vec<T::Output> = stdin.lock().lines().take(n).map(|line_result| {
        let line = line_result.expect("read from stdin failed");
        T::read_line(&line).unwrap()
    }).collect();
    if result.len() < n {
        panic!("expected reading {} lines, but only {} lines are read",
               n, result.len());
    }
    result
}
#[macro_export]
macro_rules! readn_loop {
    ( $n:expr, |$pat:pat = $t:ty| $body:expr ) => {
        {
            use std::io::BufRead;
            let stdin = std::io::stdin();
            let mut lock = stdin.lock();
            for _ in 0..$n {
                let mut line = String::new();
                lock.read_line(&mut line).expect("read from stdin failed");
                let $pat = <$t>::read_line(&line).unwrap();
                $body
            }
        }
    };
    ( $n:expr, |$($pat:pat = $t:ty),*| $body:expr ) => {
        readn_loop!($n, |($($pat),*) = ($($t),*)| $body);
    };
}
pub trait Words {
    fn read<T: Readable>(&self) -> T::Output;
}
impl<'a> Words for [&'a str] {
    fn read<T: Readable>(&self) -> T::Output {
        T::read_words(self).unwrap()
    }
}
impl<'a> Words for &'a str {
    fn read<T: Readable>(&self) -> T::Output {
        T::read_words(&[self]).unwrap()
    }
}

// SNIPPET iter
#[derive(Clone)]
pub struct StepBy<I> {
    iter: I,
    step: usize,
    first_take: bool
}
impl<I: Iterator> Iterator for StepBy<I> {
    type Item = I::Item;
    fn next(&mut self) -> Option<Self::Item> {
        if self.first_take {
            self.first_take = false;
            self.iter.next()
        } else {
            self.iter.nth(self.step)
        }
    }
}
pub struct Chunks<I: Iterator> {
    iter: I,
    size: usize
}
impl<I: Iterator> Iterator for Chunks<I> {
    type Item = Vec<I::Item>;
    fn next(&mut self) -> Option<Self::Item> {
        let first = self.iter.next();
        if first.is_none() {
            return None;
        }
        let mut chunk = Vec::with_capacity(self.size);
        chunk.push(first.unwrap());
        for _ in 0..self.size-1 {
            match self.iter.next() {
                Some(x) => chunk.push(x),
                None => break
            }
        }
        Some(chunk)
    }
}
#[derive(Clone)]
pub struct LScan<I: Iterator, S: Clone, F: FnMut(&S, I::Item) -> S> {
    iter: I,
    state: Option<S>,
    f: F,
}
impl<I: Iterator, S: Clone, F> Iterator for LScan<I, S, F>
where
    F: FnMut(&S, I::Item) -> S,
{
    type Item = S;
    fn next(&mut self) -> Option<S> {
        if self.state.is_none() {
            return None;
        }
        let state_inner = self.state.take().unwrap();
        if let Some(item) = self.iter.next() {
            self.state = Some((self.f)(&state_inner, item));
        }
        Some(state_inner)
    }
}
pub struct Flatten<I: Iterator>
where
    I::Item: IntoIterator
{
    outer_iter: I,
    inner_iter: Option<<<I as Iterator>::Item as IntoIterator>::IntoIter>
}
impl<I, J> Iterator for Flatten<I>
where
    I: Iterator<Item = J>,
    J: IntoIterator
{
    type Item = <<J as IntoIterator>::IntoIter as Iterator>::Item;
    fn next(&mut self) -> Option<J::Item> {
        loop {
            if let Some(inner_iter) = self.inner_iter.as_mut() {
                if let item@Some(_) = inner_iter.next() {
                    return item
                }
            }
            match self.outer_iter.next() {
                None => return None,
                Some(inner) => self.inner_iter = Some(inner.into_iter())
            }
        }
    }
}
/*
impl<I, J> DoubleEndedIterator for Flatten<I>
where
    I: DoubleEndedIterator,
    J: DoubleEndedIterator,
    I::Item: J {}
*/
pub struct GroupBy<K: Eq, I: Iterator, F: FnMut(&I::Item) -> K> {
    cur: Option<(I::Item, K)>,
    iter: I,
    key_fn: F
}
impl<K: Eq, I: Iterator, F: FnMut(&I::Item) -> K> Iterator for GroupBy<K, I, F> {
    type Item = (K, Vec<I::Item>);
    fn next(&mut self) -> Option<(K, Vec<I::Item>)> {
        let cur = self.cur.take();
        cur.map(|(item, key)| {
            let mut group = vec![item];
            loop {
                let next = self.iter.next();
                match next {
                    Some(next_item) => {
                        let next_key = (self.key_fn)(&next_item);
                        if key == next_key {
                            group.push(next_item);
                        } else {
                            self.cur = Some((next_item, next_key));
                            break;
                        }
                    }
                    None => {
                        self.cur = None;
                        break;
                    }
                }
            }
            (key, group)
        })
    }
}
pub struct RunLength<I: Iterator> {
    cur: Option<I::Item>,
    iter: I
}
impl<I: Iterator> Iterator for RunLength<I> where I::Item: Eq {
    type Item = (I::Item, usize);
    fn next(&mut self) -> Option<(I::Item, usize)> {
        let cur = self.cur.take();
        cur.map(|value| {
            let mut length = 1;
            loop {
                let next = self.iter.next();
                match next {
                    Some(next_value) => {
                        if value == next_value {
                            length += 1;
                        } else {
                            self.cur = Some(next_value);
                            break;
                        }
                    }
                    None => {
                        self.cur = None;
                        break;
                    }
                }
            }
            (value, length)
        })
    }
}
pub trait IteratorExt: Iterator {
    fn step_by_(self, step: usize) -> StepBy<Self> where Self: Sized {
        assert_ne!(step, 0);
        StepBy {
            iter: self,
            step: step - 1,
            first_take: true
        }
    }
    fn for_each<F: FnMut(Self::Item)>(self, mut f: F) where Self: Sized {
        for item in self {
            f(item);
        }
    }
    fn chunks(self, size: usize) -> Chunks<Self> where Self: Sized {
        assert!(size > 0);
        Chunks {
            iter: self,
            size: size
        }
    }
    fn lscan<S: Clone, F>(self, state: S, f: F) -> LScan<Self, S, F>
    where
        Self: Sized,
        F: FnMut(&S, Self::Item) -> S,
    {
        LScan {
            iter: self,
            state: Some(state),
            f: f,
        }
    }
    fn lscan1<F>(mut self, f: F) -> Option<LScan<Self, Self::Item, F>>
    where
        Self: Sized,
        Self::Item: Clone,
        F: FnMut(&Self::Item, Self::Item) -> Self::Item
    {
        self.next().map(|first| self.lscan(first, f))
    }
    fn get_unique(mut self) -> Option<Self::Item> where Self: Sized, Self::Item: Eq {
        let first_opt = self.next();
        first_opt.and_then(|first| {
            if self.all(|item| item == first) { Some(first) } else { None }
        })
    }
    fn flatten(mut self) -> Flatten<Self> where Self: Sized, Self::Item: IntoIterator {
        let inner_opt = self.next();
        Flatten {
            outer_iter: self,
            inner_iter: inner_opt.map(|inner| inner.into_iter())
        }
    }
    fn group_by<K: Eq, F: FnMut(&Self::Item) -> K>(mut self, mut f: F) -> GroupBy<K, Self, F> where Self: Sized {
        let next = self.next();
        GroupBy {
            cur: next.map(|item| {
                let key = f(&item);
                (item, key)
            }),
            iter: self,
            key_fn: f
        }
    }
    fn run_length(mut self) -> RunLength<Self> where Self: Sized, Self::Item: Eq {
        RunLength {
            cur: self.next(),
            iter: self
        }
    }
    fn join<T: std::fmt::Display>(mut self, sep: T) -> String
    where
        Self: Sized, Self::Item: std::fmt::Display
    {
        let mut result = String::new();
        if let Some(first) = self.next() {
            result.push_str(&format!("{}", first));
        }
        for s in self {
            result.push_str(&format!("{}{}", sep, s));
        }
        result
    }
    fn cat(self) -> String where Self: Sized, Self::Item: std::fmt::Display { self.join("") }
}
impl<I: Iterator> IteratorExt for I {}
pub struct Unfold<T, F> where F: FnMut(&T) -> Option<T> {
    state: Option<T>,
    f: F
}
impl<T, F> Iterator for Unfold<T, F> where F: FnMut(&T) -> Option<T> {
    type Item = T;
    fn next(&mut self) -> Option<T> {
        if self.state.is_none() {
            return None;
        }
        let state_inner = self.state.take().unwrap();
        self.state = (self.f)(&state_inner);
        Some(state_inner)
    }
}
pub fn unfold<T, F>(init: T, f: F) -> Unfold<T, F> where F: FnMut(&T) -> Option<T> {
    Unfold { state: Some(init), f: f }
}
pub struct Iterate<T, F> where F: FnMut(&T) -> T {
    state: T,
    f: F
}
impl<T, F> Iterator for Iterate<T, F> where F: FnMut(&T) -> T {
    type Item = T;
    fn next(&mut self) -> Option<T> {
        use std::mem::swap;
        let mut state = (self.f)(&self.state);
        swap(&mut state, &mut self.state);
        Some(state)
    }
}
pub fn iterate<T, F>(init: T, f: F) -> Iterate<T, F>
where
    F: FnMut(&T) -> T,
{
    Iterate { state: init, f: f }
}

// SNIPPET dbg
#[macro_export]
#[cfg(local)]
macro_rules! dbg {
    () => {
        {
            use std::io::{self, Write};
            writeln!(io::stderr(), "{}: dbg", line!()).unwrap();
        }
    };
    ($e: expr) => {
        {
            use std::io::{self, Write};
            let result = $e;
            writeln!(io::stderr(), "{}: {} = {:?}",
                     line!(), stringify!($e), result)
                .unwrap();
            result
        }
    }
}
#[macro_export]
#[cfg(not(local))]
macro_rules! dbg {
    () => {};
    ($e: expr) => {
        { $e }
    }
}

// SNIPPET cmp
use std::cmp::{Ord, Ordering};
pub fn minmax<T: Ord>(a: T, b: T) -> (T, T) {
    if a <= b { (a, b) } else { (b, a) }
}
#[macro_export]
macro_rules! chmin {
    ($place: expr, $expr: expr) => {
        let value = $expr;
        if value < $place {
            $place = value;
        }
    }
}
#[macro_export]
macro_rules! chmax {
    ($place: expr, $expr: expr) => {
        let value = $expr;
        if value > $place {
            $place = value;
        }
    }
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Hash)]
pub struct Reverse<T: Ord>(pub T);
impl<T: Ord> PartialOrd for Reverse<T> {
    fn partial_cmp(&self, other: &Reverse<T>) -> Option<Ordering> {
        other.0.partial_cmp(&self.0)
    }
}
impl<T: Ord> Ord for Reverse<T> {
    fn cmp(&self, other: &Reverse<T>) -> Ordering {
        other.0.cmp(&self.0)
    }
}
pub trait SortDesc<T> {
    fn sort_desc(&mut self) where T: Ord;
    fn sort_desc_by_key<K: Ord, F: FnMut(&T) -> K>(&mut self, f: F);
}
impl<T> SortDesc<T> for [T] {
    fn sort_desc(&mut self) where T: Ord {
        self.sort();
        self.reverse();
    }
    fn sort_desc_by_key<K: Ord, F: FnMut(&T) -> K>(&mut self, mut f: F) {
        self.sort_by_key(|x| Reverse(f(x)));
    }
}
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug, Default, Hash)]
pub struct Total<T: PartialOrd + PartialEq>(pub T);
impl<T: PartialOrd + PartialEq> Eq for Total<T> {}
impl<T: PartialOrd + PartialEq> Ord for Total<T> {
    fn cmp(&self, other: &Self) -> Ordering {
        self.partial_cmp(other).unwrap()
    }
}
pub trait IteratorMinmax: Iterator {
    fn minmax(self) -> Option<(Self::Item, Self::Item)>;
    fn minmax_by_key<K, F>(self, key_fn: F) -> Option<(Self::Item, Self::Item)>
    where
        K: Ord,
        F: FnMut(&Self::Item) -> K;
    fn minmax_by<F>(self, compare: F) -> Option<(Self::Item, Self::Item)>
    where
        F: FnMut(&Self::Item, &Self::Item) -> Ordering;
}
impl<T: Ord + Clone, I: Iterator<Item=T>> IteratorMinmax for I {
    fn minmax(self) -> Option<(Self::Item, Self::Item)> {
        self.minmax_by(|a, b| a.cmp(b))
    }
    fn minmax_by_key<K, F>(self, mut key_fn: F) -> Option<(Self::Item, Self::Item)>
    where
        K: Ord,
        F: FnMut(&Self::Item) -> K
    {
        self.minmax_by(|a, b| key_fn(a).cmp(&key_fn(b)))
    }
    fn minmax_by<F>(mut self, mut compare: F) -> Option<(Self::Item, Self::Item)>
    where
        F: FnMut(&Self::Item, &Self::Item) -> Ordering
    {
        let first = match self.next() {
            Some(x) => x,
            None => return None
        };
        let second = match self.next() {
            Some(x) => x,
            None => return Some((first.clone(), first))
        };
        let mut result = minmax(first, second);
        while let Some(x) = self.next() {
            let (min, max) = result;
            result = if compare(&x, &min) == Ordering::Less {
                (x, max)
            } else if compare(&max, &x) == Ordering::Less {
                (min, x)
            } else {
                (min, max)
            };
        }
        Some(result)
    }
}

// END SNIPPETS

use std::iter::repeat;
use std::cmp;

#[derive(Clone, Debug)]
struct C(char);

impl PartialEq for C {
    fn eq(&self, x: &C) -> bool {
        self.0 == '?' || x.0 == '?' || self.0 == x.0
    }
}

impl Eq for C {}

fn combine(a: &[C], b: &[C], q_len: usize, i: usize) -> Vec<C> {
    let mut res: Vec<C> = repeat(C('?')).take(q_len)
        .chain(a.iter().cloned())
        .chain(repeat(C('?')).take(q_len))
        .collect();
    for j in 0..b.len() {
        if res[i+j].0 == '?' {
            res[i+j] = b[j].clone();
        }
    }
    let beg = res.iter().position(|c| c.0 != '?');
    let end =  res.iter().rev().position(|c| c.0 != '?').map(|p| res.len()-p);
    match (beg, end) {
        (Some(beg), Some(end)) => res[beg..end].to_vec(),
        _ => vec![]
    }
}

fn matches(text: &[C], pat: &[C], text_beg: usize) -> bool {
    &text[text_beg .. text_beg+pat.len()] == pat
}

fn main() {
    readls!(a = String, b = String, c = String);
    let a: Vec<C> = a.chars().map(|c| C(c)).collect();
    let b: Vec<C> = b.chars().map(|c| C(c)).collect();
    let c: Vec<C> = c.chars().map(|c| C(c)).collect();

    let mut ans = 1_000_000;
    let text1: Vec<C> = repeat(C('?')).take(b.len()+c.len())
        .chain(a.iter().cloned())
        .chain(repeat(C('?')).take(b.len()+c.len()))
        .collect();
    dbg!(&text1);
    for i in 0..text1.len() - b.len() {
        if !matches(&text1, &b, i) {
            continue;
        }
        let mid = combine(&a, &b, b.len()+c.len(), i);
        dbg!((i, &mid));
        let text2: Vec<C> = repeat(C('?')).take(c.len())
            .chain(mid.iter().cloned())
            .chain(repeat(C('?')).take(c.len()))
            .collect();
        for j in text2.z_match_indices(&c) {
            if !matches(&text2, &c, j) {
                continue;
            }
            dbg!(j);
            let len = if j < c.len() {
                mid.len() + c.len() - j
            } else if j+c.len() <= c.len() + mid.len() {
                mid.len()
            } else {
                mid.len() + j - mid.len()
            };
            chmin!(ans, len);
        }
    }
    let min = cmp::max(a.len(), cmp::max(b.len(), c.len()));
    println!("{}", cmp::max(ans, min));
}