#![warn(clippy::pedantic)]
use nom::{
    bytes::complete::{tag, take_till},
    character::complete::{anychar, newline},
    combinator::recognize,
    multi::many1,
    sequence::{pair, separated_pair, terminated},
};
use std::collections::BTreeMap;
use std::io::{stdin, Read};
use std::ops::Add;

type Input<'a> = &'a str;
type IResult<'a, T> = nom::IResult<Input<'a>, T>;

type Rule = ((char, char), char);

fn rule(i: Input) -> IResult<Rule> {
    separated_pair(pair(anychar, anychar), tag(" -> "), anychar)(i)
}

fn parse_input(i: Input) -> IResult<(&str, Vec<Rule>)> {
    separated_pair(
        terminated(recognize(take_till(|c| c == '\n')), newline),
        newline,
        many1(terminated(rule, newline)),
    )(i)
}

#[derive(Clone, Debug, PartialEq, Eq)]
struct Counter<T> {
    counts: BTreeMap<T, usize>,
}

impl<T: Ord> Counter<T> {
    pub fn new() -> Self {
        Self {
            counts: BTreeMap::new(),
        }
    }

    pub fn push(&mut self, el: T) {
        *self.counts.entry(el).or_insert(0) += 1;
    }

    pub fn most_common(&self) -> Option<(&T, usize)> {
        self.counts
            .iter()
            .map(|(el, count)| (el, *count))
            .max_by_key(|&(_, count)| count)
    }

    pub fn least_common(&self) -> Option<(&T, usize)> {
        self.counts
            .iter()
            .map(|(el, count)| (el, *count))
            .min_by_key(|&(_, count)| count)
    }
}

impl<T: Ord> FromIterator<T> for Counter<T> {
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
        let mut counts = Self::new();
        for el in iter {
            counts.push(el);
        }
        counts
    }
}

impl<T: Ord + Copy> Add<&Counter<T>> for Counter<T> {
    type Output = Self;

    fn add(mut self, other: &Self) -> Self {
        for (el, count) in &other.counts {
            *self.counts.entry(*el).or_insert(0) += count;
        }
        self
    }
}

impl<T: Ord> Add for Counter<T> {
    type Output = Self;

    fn add(mut self, other: Self) -> Self {
        for (el, count) in other.counts {
            *self.counts.entry(el).or_insert(0) += count;
        }
        self
    }
}

#[derive(Clone, Debug)]
struct LetterCounter {
    rules: BTreeMap<(char, char), char>,
    counts: BTreeMap<(char, char, usize), Counter<char>>,
}

impl LetterCounter {
    pub fn new(rules: BTreeMap<(char, char), char>) -> Self {
        let mut counts = BTreeMap::new();
        for &(left, right) in rules.keys() {
            counts.insert((left, right, 0), Counter::from_iter([left]));
        }
        Self { rules, counts }
    }

    pub fn get_counts_right_exclusive(
        &mut self,
        left: char,
        right: char,
        depth: usize,
    ) -> &Counter<char> {
        #[allow(clippy::map_entry)] // lifetimes don't work out
        if !self.counts.contains_key(&(left, right, depth)) {
            let middle = self.rules[&(left, right)];
            let counts_left = self
                .get_counts_right_exclusive(left, middle, depth - 1)
                .clone();
            let counts_right = self.get_counts_right_exclusive(middle, right, depth - 1);
            let counts = counts_left + counts_right;
            self.counts.insert((left, right, depth), counts);
        }
        &self.counts[&(left, right, depth)]
    }
}

fn main() {
    let mut input = String::new();
    stdin().lock().read_to_string(&mut input).unwrap();

    let (input, rules) = parse_input(&input).unwrap().1;

    let rules: BTreeMap<_, _> = rules.into_iter().collect();
    let chars: Vec<_> = input.chars().collect();

    let mut counter = LetterCounter::new(rules);

    let mut run = |steps| {
        let mut totals = chars.windows(2).fold(Counter::new(), |counts, x| {
            counts + counter.get_counts_right_exclusive(x[0], x[1], steps)
        });
        totals.push(*chars.last().unwrap());

        println!(
            "{:?}",
            totals.most_common().unwrap().1 - totals.least_common().unwrap().1
        );
    };
    run(10);
    run(40);
}
