Problem:
Score : 700 points

Problem Statement

You are given a string S consisting of a,b and c. Find the number of strings that can be possibly obtained by repeatedly performing the following operation zero or more times, modulo 998244353:

Choose an integer i such that 1\leq i\leq |S|-1 and the i-th and (i+1)-th characters in S are different. Replace each of the i-th and (i+1)-th characters in S with the character that differs from both of them (among a, b and c).

Constraints

2 \leq |S| \leq 2 × 10^5

S consists of a, b and c.

Input

Input is given from Standard Input in the following format:

S

Output

Print the number of strings that can be possibly obtained by repeatedly performing the operation, modulo 998244353.

Sample Input 1

abc

Sample Output 1

3

abc, aaa and ccc can be obtained.

Sample Input 2

abbac

Sample Output 2

65

Sample Input 3

babacabac

Sample Output 3

6310

Sample Input 4

ababacbcacbacacbcbbcbbacbaccacbacbacba

Sample Output 4

148010497

Solution in Rust (1.42.0) (judge verdict: Accepted)

use proconio::input;

// modint {{{
mod modint {
    pub trait ModValue:
        std::ops::Add<Output = Self>
        + std::ops::Sub<Output = Self>
        + std::ops::Mul<Output = Self>
        + std::ops::Div<Output = Self>
        + std::ops::Rem<Output = Self>
        + std::ops::AddAssign
        + std::ops::SubAssign
        + std::ops::MulAssign
        + std::ops::DivAssign
        + std::ops::RemAssign
        + std::ops::Neg
        + std::cmp::Ord
        + Clone
        + Copy
        + Sized
        + std::fmt::Debug
        + std::fmt::Display
    {
    }

    impl<
            Value: std::ops::Add<Output = Self>
                + std::ops::Sub<Output = Self>
                + std::ops::Mul<Output = Self>
                + std::ops::Div<Output = Self>
                + std::ops::Rem<Output = Self>
                + std::ops::AddAssign
                + std::ops::SubAssign
                + std::ops::MulAssign
                + std::ops::DivAssign
                + std::ops::RemAssign
                + std::ops::Neg
                + std::cmp::Ord
                + Clone
                + Copy
                + Sized
                + std::fmt::Debug
                + std::fmt::Display,
        > ModValue for Value
    {
    }

    pub trait Minfo: Clone + Copy + std::fmt::Debug {
        type Value: ModValue;

        fn modulus() -> Self::Value;

        fn zero() -> Self::Value;

        fn one() -> Self::Value;
    }

    #[derive(Clone, Copy, Debug)]
    pub struct Mint<Mod: Minfo>(Mod::Value);

    impl<Mod: Minfo> Mint<Mod> {
        #[inline]
        #[allow(dead_code)]
        fn is_within_the_range(x: Mod::Value) -> bool {
            Mod::zero() <= x && x < Mod::modulus()
        }

        #[inline]
        #[allow(dead_code)]
        fn normalize(x: Mod::Value) -> Mod::Value {
            if Self::is_within_the_range(x) {
                x
            } else {
                let x = x % Mod::modulus();
                if x < Mod::zero() {
                    x + Mod::modulus()
                } else {
                    x
                }
            }
        }

        #[allow(dead_code)]
        pub fn zero() -> Self {
            Self(Mod::zero())
        }

        #[allow(dead_code)]
        pub fn one() -> Self {
            Self(Mod::one())
        }

        #[allow(dead_code)]
        pub fn modulus() -> Mod::Value {
            Mod::modulus()
        }

        #[allow(dead_code)]
        pub fn is_zero(&self) -> bool {
            self.value() == Mod::zero()
        }

        #[allow(dead_code)]
        pub fn from_value(x: Mod::Value) -> Self {
            Self(Self::normalize(x))
        }

        #[allow(dead_code)]
        pub fn value(&self) -> Mod::Value {
            self.0
        }

        #[allow(dead_code)]
        pub fn inv(&self) -> Self {
            if self.is_zero() {
                panic!("inv(0)");
            }
            Self(Self::raw_inv(self.value()))
        }

        #[allow(dead_code)]
        pub fn pow(&self, b: u64) -> Self {
            Self(Self::raw_pow(self.value(), b))
        }

        fn raw_add(x: Mod::Value, y: Mod::Value) -> Mod::Value {
            let z = x + y;
            if Mod::modulus() <= z {
                z - Mod::modulus()
            } else {
                z
            }
        }

        fn raw_sub(x: Mod::Value, y: Mod::Value) -> Mod::Value {
            let z = x - y;
            if z < Mod::zero() {
                z + Mod::modulus()
            } else {
                z
            }
        }

        fn raw_neg(x: Mod::Value) -> Mod::Value {
            if x == Mod::zero() {
                Mod::zero()
            } else {
                Mod::modulus() - x
            }
        }

        fn raw_mul(x: Mod::Value, y: Mod::Value) -> Mod::Value {
            x * y % Mod::modulus()
        }

        fn raw_div(x: Mod::Value, y: Mod::Value) -> Mod::Value {
            Self::raw_mul(x, Self::raw_inv(y))
        }

        #[allow(clippy::many_single_char_names)]
        fn raw_inv(mut x: Mod::Value) -> Mod::Value {
            let mut y = Mod::modulus();
            let mut u = Mod::one();
            let mut v = Mod::zero();
            while x != Mod::zero() {
                let q = y / x;
                y -= x * q;
                v -= u * q;
                std::mem::swap(&mut x, &mut y);
                std::mem::swap(&mut u, &mut v);
            }
            Self::normalize(v)
        }

        fn raw_pow(mut a: Mod::Value, mut b: u64) -> Mod::Value {
            let mut x = Mod::one();
            while 0 < b {
                if b % 2 == 1 {
                    x = Self::raw_mul(x, a);
                }
                a = Self::raw_mul(a, a);
                b /= 2
            }
            x
        }
    }

    macro_rules! impl_biop {
        (
            $(
                $biop_trait: ident::$biop_fn: ident,
                $biop_assign_trait: ident::$biop_assign_fn: ident,
                $biop_impl: ident;
            )*
        ) => {
            $(
                impl<Mod: Minfo> std::ops::$biop_trait for Mint<Mod> {
                    type Output = Self;
                    fn $biop_fn(self, rhs: Self) -> Self::Output {
                        Mint(Self::$biop_impl(self.value(), rhs.value()))
                    }
                }
                impl<Mod: Minfo> std::ops::$biop_assign_trait for Mint<Mod>
                where
                    Self: Copy
                {
                    fn $biop_assign_fn(&mut self, rhs: Self) {
                        use std::ops::$biop_trait;
                        *self = Mint::$biop_fn(*self, rhs)
                    }
                }
            )*
        };
    }

    impl_biop! {
        Add::add, AddAssign::add_assign, raw_add;
        Sub::sub, SubAssign::sub_assign, raw_sub;
        Mul::mul, MulAssign::mul_assign, raw_mul;
        Div::div, DivAssign::div_assign, raw_div;
    }

    impl<Mod: Minfo> std::ops::Neg for Mint<Mod> {
        type Output = Self;
        fn neg(self) -> Self::Output {
            Self(Self::raw_neg(self.value()))
        }
    }

    impl<Mod: Minfo> std::cmp::PartialEq for Mint<Mod> {
        fn eq(&self, other: &Self) -> bool {
            self.value().eq(&other.value())
        }
    }
    impl<Mod: Minfo> std::cmp::Eq for Mint<Mod> {}

    impl<Mod: Minfo> std::fmt::Display for Mint<Mod> {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            write!(f, "{}", self.value())
        }
    }

    impl<Mod: Minfo> std::iter::Sum<Self> for Mint<Mod> {
        fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
            iter.fold(Mint::zero(), |acc, x| acc + x)
        }
    }

    impl<Mod: Minfo> std::iter::Product<Self> for Mint<Mod> {
        fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
            iter.fold(Mint::one(), |acc, x| acc * x)
        }
    }

    #[allow(dead_code)]
    pub struct Factorial<Mod: Minfo> {
        normal: Vec<Mint<Mod>>,
        inverted: Vec<Mint<Mod>>,
    }

    #[allow(dead_code)]
    impl<Mod: Minfo> Factorial<Mod>
    where
        Mod::Value: std::convert::TryFrom<usize, Error = std::num::TryFromIntError>,
    {
        pub fn new(n: usize) -> Self {
            use std::convert::TryFrom;
            let mut normal = vec![Mint::one(); n];
            for i in 1..n {
                normal[i] = normal[i - 1] * Mint::from_value(Mod::Value::try_from(i).unwrap());
            }
            let mut inverted = vec![Mint::one(); n];
            inverted[n - 1] = normal[n - 1].inv();
            for i in (1..n).rev() {
                inverted[i - 1] = inverted[i] * Mint::from_value(Mod::Value::try_from(i).unwrap());
            }
            Self { normal, inverted }
        }
    }

    #[allow(dead_code)]
    impl<Mod: Minfo> Factorial<Mod> {
        pub fn inverted(&self, i: usize) -> Mint<Mod> {
            self.inverted[i]
        }
    }

    #[allow(dead_code)]
    impl<Mod: Minfo, I: std::slice::SliceIndex<[Mint<Mod>]>> std::ops::Index<I> for Factorial<Mod> {
        type Output = I::Output;

        #[inline]
        fn index(&self, index: I) -> &Self::Output {
            &self.normal[index]
        }
    }

    #[allow(dead_code)]
    pub struct DoubleFactorial<Mod: Minfo> {
        normal: Vec<Mint<Mod>>,
        inverted: Vec<Mint<Mod>>,
    }

    #[allow(dead_code)]
    impl<Mod: Minfo> DoubleFactorial<Mod>
    where
        Mod::Value: std::convert::TryFrom<usize, Error = std::num::TryFromIntError>,
    {
        pub fn new(n: usize) -> Self {
            use std::convert::TryFrom;
            let mut normal = vec![Mint::one(); n];
            for i in 2..n {
                normal[i] = normal[i - 2] * Mint::from_value(Mod::Value::try_from(i).unwrap());
            }
            let mut inverted = vec![Mint::one(); n];
            inverted[n - 1] = normal[n - 1].inv();
            inverted[n - 2] = normal[n - 2].inv();
            for i in (2..n).rev() {
                inverted[i - 2] = inverted[i] * Mint::from_value(Mod::Value::try_from(i).unwrap());
            }
            Self { normal, inverted }
        }
    }

    #[allow(dead_code)]
    impl<Mod: Minfo> DoubleFactorial<Mod> {
        pub fn inverted(&self, i: usize) -> Mint<Mod> {
            self.inverted[i]
        }
    }

    #[allow(dead_code)]
    impl<Mod: Minfo, I: std::slice::SliceIndex<[Mint<Mod>]>> std::ops::Index<I>
        for DoubleFactorial<Mod>
    {
        type Output = I::Output;

        #[inline]
        fn index(&self, index: I) -> &Self::Output {
            &self.normal[index]
        }
    }

    #[macro_export]
    macro_rules! define_mint {
        (
            struct $modint: ident ($modinfo: ident ($modvalue: expr; $modty: ty));
        ) => {
            #[allow(dead_code)]
            #[derive(Clone, Copy, Debug)]
            pub struct $modinfo {}
            impl Minfo for $modinfo {
                type Value = $modty;
                fn modulus() -> Self::Value {
                    $modvalue
                }
                fn zero() -> Self::Value {
                    0
                }
                fn one() -> Self::Value {
                    1
                }
            }
            type $modint = Mint<$modinfo>;
        };
    }

    #[allow(dead_code)]
    #[derive(Clone, Copy, Debug)]
    pub struct Minfo1000000007 {}

    impl Minfo for Minfo1000000007 {
        type Value = i64;
        fn modulus() -> Self::Value {
            1_000_000_007
        }
        fn zero() -> Self::Value {
            0
        }
        fn one() -> Self::Value {
            1
        }
    }

    #[allow(dead_code)]
    pub type Mint1000000007 = Mint<Minfo1000000007>;

    #[allow(dead_code)]
    #[derive(Clone, Copy, Debug)]
    pub struct Minfo998244353 {}

    impl Minfo for Minfo998244353 {
        type Value = i64;
        fn modulus() -> Self::Value {
            998_244_353
        }
        fn zero() -> Self::Value {
            0
        }
        fn one() -> Self::Value {
            1
        }
    }

    #[allow(dead_code)]
    pub type Mint998244353 = Mint<Minfo998244353>;
}
// }}}

type Mint = modint::Mint998244353;

#[allow(dead_code)]
fn change(x: usize) -> usize {
    assert!(x < 9 && x % 4 != 0);
    ((6 - (x % 3) - (x / 3)) % 3) * 4
}

#[allow(dead_code)]
fn translate(s: &str) -> usize {
    std::iter::successors(Some(1), |x| Some(x * 3))
        .zip(s.chars().rev())
        .map(|(coeff, c)| coeff * ((c as usize) - b'a' as usize))
        .sum::<usize>()
}

#[allow(dead_code)]
fn reverse_translate(n: usize, mut x: usize) -> String {
    let mut ans = vec![b'$'; n];
    for i in (0..n).rev() {
        ans[i] = b'a' + (x % 3) as u8;
        x /= 3;
    }
    String::from_utf8(ans).unwrap()
}

#[allow(dead_code)]
fn brute_force(s: &str) -> usize {
    let n = s.len();
    let x = translate(s);
    let n_three = std::iter::successors(Some(1), |x| Some(x * 3))
        .take(n + 1)
        .last()
        .unwrap();
    let mut ckd = vec![false; n_three];
    fn dfs(n: usize, x: usize, ckd: &mut [bool]) {
        ckd[x] = true;
        for d in std::iter::successors(Some(1), |x| Some(3 * x)).take(n - 1) {
            let r = (x / d) % 9;
            if r % 4 == 0 {
                continue;
            }
            let y = x - d * r + d * change(r);
            if !ckd[y] {
                dfs(n, y, ckd);
            }
        }
    }
    dfs(n, x, &mut ckd);
    ckd.into_iter().filter(|&x| x).count()
}

fn count_adjacently_distinct(n: usize, res: usize) -> Mint {
    // dp[i][j][k] j: sum k: last
    let mut dp = vec![[[Mint::zero(); 3]; 3]; n + 1];
    (0..3).for_each(|x| dp[1][x][x] = Mint::one());
    for i in 1..n {
        for j in 0..3 {
            for k in 0..3 {
                let x = dp[i][j][k];
                for k1 in (0..3).filter(|&k1| k1 != k) {
                    dp[i + 1][(j + k1) % 3][k1] += x;
                }
            }
        }
    }

    dp[n][res].iter().copied().sum::<Mint>()
}

fn is_adjacently_distince(s: &str) -> bool {
    let n = s.len();
    let s = s.as_bytes();
    (0..n - 1).all(|i| s[i] != s[i + 1])
}

fn solve_for_long_s(s: &str) -> Mint {
    if s == String::from_utf8(vec![*s.as_bytes().first().unwrap(); s.len()]).unwrap() {
        Mint::one()
    } else {
        let n = s.len();
        let all = Mint::from_value(3).pow((n - 1) as u64);
        let res = s
            .as_bytes()
            .iter()
            .map(|c| (c - b'a') as usize)
            .sum::<usize>()
            % 3;
        let adjacently_distinct = count_adjacently_distinct(n, res);

        if is_adjacently_distince(s) {
            all - adjacently_distinct + Mint::one()
        } else {
            all - adjacently_distinct
        }
    }
}

fn main() {
    input!(s: String);
    let n = s.len();
    println!(
        "{}",
        if n < 7 {
            Mint::from_value(brute_force(&s) as i64)
        } else {
            solve_for_long_s(&s)
        }
    );
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_change() {
        assert_eq!(change(1), 8);
        assert_eq!(change(2), 4);
        assert_eq!(change(5), 0);
        assert_eq!(change(3), 8);
        assert_eq!(change(6), 4);
        assert_eq!(change(7), 0);
    }

    #[test]
    fn test_translate() {
        let v = [
            "aaa", "aab", "aac", "aba", "abb", "abc", "aca", "acb", "acc", "baa", "bab", "bac",
            "bba", "bbb", "bbc", "bca", "bcb", "bcc",
        ];
        for (i, s) in v.iter().enumerate() {
            assert_eq!(i, translate(s));
            assert_eq!(reverse_translate(3, i), *s);
        }
    }

    #[test]
    fn test_bruteforce() {
        assert_eq!(brute_force("aa"), 1);
        assert_eq!(brute_force("ab"), 2);
        assert_eq!(brute_force("abc"), 3);
        assert_eq!(brute_force("aaaa"), 1);
        assert_eq!(brute_force("aaab"), 19);
        assert_eq!(brute_force("aabc"), 19);
        assert_eq!(brute_force("bcba"), 20);
        assert_eq!(brute_force("bcba"), 20);
        assert_eq!(brute_force("abbac"), 65);
        assert_eq!(brute_force("babacabac"), 6310);

        for x in 0..81 {
            [1, 19, 20]
                .iter()
                .find(|&&y| y == dbg!(brute_force(dbg!(&reverse_translate(4, x)))))
                .unwrap();
        }

        for x in 0..27 {
            [1, 3, 6, 7]
                .iter()
                .find(|&&y| y == dbg!(brute_force(dbg!(&reverse_translate(3, x)))))
                .unwrap();
        }
    }

    #[test]
    fn test_compare() {
        for n in 4..7 {
            for x in 0..std::iter::successors(Some(1), |x| Some(x * 3))
                .take(n + 1)
                .last()
                .unwrap()
            {
                let s = reverse_translate(n, x);
                let brute_force = brute_force(&s);
                let fast = solve_for_long_s(&s);
                eprintln!(
                    "x = {}, s = {}, brute_force = {}, fast = {}",
                    x, &s, brute_force, fast
                );
                assert_eq!(Mint::from_value(brute_force as i64), fast);
            }
        }
    }
}