use std::cmp::*;
use input_mcr::*;
use std::collections::{HashMap,BTreeMap};

use xorshift::*;

// use std::collections::HashMap;
use std::hash::{Hasher, BuildHasherDefault};
pub struct IdHasher(u64);

impl Default for IdHasher {

    #[inline]
    fn default() -> IdHasher {
        IdHasher(0)
    }
}

impl Hasher for IdHasher {
    #[inline]
    fn finish(&self) -> u64 {
        self.0
    }

    #[inline]
    fn write(&mut self, bytes: &[u8]) {
        assert_eq!(bytes.len(), 8);
        let res = unsafe {
            *(bytes.as_ptr() as *const u64)
        };
        *self = IdHasher(res);
    }
}

pub type HashedU64Map<V> = HashMap<u64, V, BuildHasherDefault<IdHasher>>;


fn main() {
    
    input! {
        n: usize,
        ss: [chars; n],
    }
    
    // let mut n = 1;
    // let mut ss = vec![vec![]];
    // for i in 0..1_000_000 {
    //     ss[0].push((i % 26 + 'a' as usize) as u8 as char);
    // }

    let mut hash = vec![vec![]; 26];
    let mut rand = XorShift128::new_with_seed(314);
    for i in 0..26 {
        for k in 0..1_000_100 {
            hash[i].push(rand.gen_u64());
        }
    }
    let mut rec = HashMap::<u64,[i64;26]>::default();
    let mut ys = vec![];
    for s in &ss {
        // eprintln!("s = {:?}", s);
        let mut exist_bits = vec![]; 
        let mut bits = 0;
        exist_bits.push(bits);
        for &c in s.iter() {
            let v = c as usize - 'a' as usize;
            bits |= 1 << v;
            exist_bits.push(bits);
        }
        let mut h = 0;
        let mut tail_h = 0;
        for i in (0..s.len()+1).rev() {
            for j in 0..26 {
                let t = exist_bits[i];
                if t & (1 << j) != 0 {
                    //eprintln!("j = {}, h = {}, += 1", j, h);
                    rec.entry(h).or_insert([0; 26])[j] += 1;
                }
            }
            if i == 1 {
                tail_h = h;
            }
            if i > 0 {
                let v = s[i-1] as usize - 'a' as usize;
                let k = s.len() - (i-1) - 1;
                h ^= hash[v][k];
            }
        }
        ys.push((s[0] as usize - 'a' as usize, tail_h));
    }
    for y in &ys {
        //eprintln!("y = {:?}", y);
    }
    let mut res = 0i64;
    for &(v,tail_h) in &ys {
        res += rec.get(&tail_h).unwrap_or(&[0; 26])[v];
    }
    res -= n as i64;
    println!("{}", res);
}
pub mod input_mcr {
    // ref: tanakh <https://qiita.com/tanakh/items/0ba42c7ca36cd29d0ac8>
    #[macro_export(local_inner_macros)]
    macro_rules! input {
        (source = $s:expr, $($r:tt)*) => {
            let mut parser = Parser::from_str($s);
            input_inner!{parser, $($r)*}
        };
        (parser = $parser:ident, $($r:tt)*) => {
            input_inner!{$parser, $($r)*}
        };
        (new_stdin_parser = $parser:ident, $($r:tt)*) => {
            let stdin = std::io::stdin();
            let reader = std::io::BufReader::new(stdin.lock());
            let mut $parser = Parser::new(reader);
            input_inner!{$parser, $($r)*}
        };
        ($($r:tt)*) => {
            input!{new_stdin_parser = parser, $($r)*}
        };
    }
    
    #[macro_export(local_inner_macros)]
    macro_rules! input_inner {
        ($parser:ident) => {};
        ($parser:ident, ) => {};
        ($parser:ident, $var:ident : $t:tt $($r:tt)*) => {
            let $var = read_value!($parser, $t);
            input_inner!{$parser $($r)*}
        };
    }
    
    #[macro_export(local_inner_macros)]
    macro_rules! read_value {
        ($parser:ident, ( $($t:tt),* )) => {
            ( $(read_value!($parser, $t)),* )
        };
        ($parser:ident, [ $t:tt ; $len:expr ]) => {
            (0..$len).map(|_| read_value!($parser, $t)).collect::<Vec<_>>()
        };
        ($parser:ident, chars) => {
            read_value!($parser, String).chars().collect::<Vec<char>>()
        };
        ($parser:ident, char_) => {
            read_value!($parser, String).chars().collect::<Vec<char>>()[0]
        };
        ($parser:ident, usize1) => {
            read_value!($parser, usize) - 1
        };
        ($parser:ident, line) => {
            $parser.next_line()
        };
        ($parser:ident, line_) => {
            $parser.next_line().chars().collect::<Vec<char>>()
        };
        ($parser:ident, $t:ty) => {
            $parser.next::<$t>().expect("Parse error")
        };
    }
    
    use std::io;
    use std::io::BufRead;
    use std::str;
    use std::collections::VecDeque;
    
    pub struct Parser<R> {
        pub reader: R,
        buf: VecDeque<u8>,
        parse_buf: Vec<u8>,
    }
    
    impl Parser<io::Empty> {
        pub fn from_str(s: &str) -> Parser<io::Empty> {
            Parser {
                reader: io::empty(),
                buf: VecDeque::from(s.as_bytes().to_vec()),
                parse_buf: vec![],
            }
        }
    }
    
    impl<R: BufRead> Parser<R> {
        pub fn new(reader: R) -> Parser<R> {
            Parser {
                reader: reader,
                buf: VecDeque::new(),
                parse_buf: vec![],
            }
        }
        pub fn update_buf(&mut self) {
            loop {
                let (len, complete) = {
                    let buf2 = self.reader.fill_buf().unwrap();
                    self.buf.extend(buf2.iter());
                    let len = buf2.len();
                    (len, buf2.last() < Some(&0x20))
                };
                self.reader.consume(len);
                if complete {
                    break;
                }
            }
        }
        pub fn next<T: str::FromStr>(&mut self) -> Result<T, T::Err> {
            loop {
                while let Some(c) = self.buf.pop_front() {
                    if c > 0x20 {
                        self.buf.push_front(c);
                        break;
                    }
                }
                self.parse_buf.clear();
                while let Some(c) = self.buf.pop_front() {
                    if c <= 0x20 {
                        self.buf.push_front(c);
                        break;
                    } else {
                        self.parse_buf.push(c);
                    }
                }
                if self.parse_buf.is_empty() {
                    self.update_buf();
                } else {
                    return unsafe { str::from_utf8_unchecked(&self.parse_buf) }.parse::<T>();
                }
            }
        }
        pub fn next_line(&mut self) -> String {
            loop {
                while let Some(c) = self.buf.pop_front() {
                    if c >= 0x20 {
                        self.buf.push_front(c);
                        break;
                    }
                }
                self.parse_buf.clear();
                while let Some(c) = self.buf.pop_front() {
                    if c < 0x20 {
                        self.buf.push_front(c);
                        break;
                    } else {
                        self.parse_buf.push(c);
                    }
                }
                if self.parse_buf.is_empty() {
                    self.update_buf();
                } else {
                    return unsafe { str::from_utf8_unchecked(&self.parse_buf) }.to_string();
                }
            }
        }
    }
}
pub mod xorshift {
    pub struct XorShift128 {
        x: u32,
        y: u32,
        z: u32,
        w: u32,
    }
    
    impl XorShift128 {
        pub fn new_with_seed(seed: u32) -> XorShift128 {
            XorShift128 {
                x: 123456789,
                y: 362436069,
                z: 521288629,
                w: seed,
            }
        }
        pub fn new() -> XorShift128 {
            XorShift128 {
                x: 123456789,
                y: 362436069,
                z: 521288629,
                w: 88675123,
            }
        }
        pub fn gen(&mut self) -> u32 {
            let t = self.x ^ (self.x << 11);
            self.x = self.y;
            self.y = self.z;
            self.z = self.w;
            self.w = (self.w ^ (self.w >> 19)) ^ (t ^ (t >> 8));
            self.w
        }
        pub fn gen_u64(&mut self) -> u64 {
            let high = (self.gen() as u64) << 32;
            let low = self.gen() as u64;
            high | low
        }
        pub fn gen_mod(&mut self, n: u32) -> u32 {
            self.gen() % n
        }
        pub fn gen_f64(&mut self) -> f64 {
            self.gen() as f64 / (1i64 << 32) as f64
        }
        pub fn shuffle<T>(&mut self, xs: &mut [T]) {
            let n = xs.len();
            for i in (1..n).rev() {
                let k = self.gen_mod(i as u32) as usize;
                xs.swap(i,k);
            }
        }
    }
}



Measured memory_bytes: 883576