#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#include <queue>
#include <string>
#include <map>
#include <set>
#include <stack>
#include <tuple>
#include <deque>
#include <numeric>
#include <bitset>
#include <iomanip>
#include <cassert>
#include <chrono>
#include <random>
#include <limits>
#include <iterator>
#include <functional>
#include <sstream>
#include <complex>
#include <cstring>
using namespace std;

using ll = long long;
using P = pair<int, int>;
constexpr int INF = 1001001001;
constexpr int mod = 1000000007;
// constexpr int mod = 998244353;

template<class T>
inline bool chmax(T& x, T y){
    if(x < y){
        x = y;
        return true;
    }
    return false;
}
template<class T>
inline bool chmin(T& x, T y){
    if(x > y){
        x = y;
        return true;
    }
    return false;
}

template<int char_size>
struct TrieNode {
    int nxt[char_size];
    int exist;
    vector<int> accept;

    TrieNode() : exist(0) { memset(nxt, -1, sizeof(nxt)); }
};

template<int char_size, int margin>
struct Trie {
    using Node = TrieNode<char_size>;

    vector<Node> nodes;
    int root;
    ll ans = 0;

    Trie() : root(0) { nodes.push_back(Node()); }

    void update_direct(int node, int id) { nodes[node].accept.push_back(id); }

    void update_child(int node, int child, int id) { ++nodes[node].exist; }
    
    void add(const string& str, int str_idx, int node_idx, int id){
        if(str_idx == str.size())   update_direct(node_idx, id);
        else{
            const int c = str[str_idx] - margin;
            if(nodes[node_idx].nxt[c] == -1){
                nodes[node_idx].nxt[c] = (int)nodes.size();
                nodes.push_back(Node());
            }
            add(str, str_idx + 1, nodes[node_idx].nxt[c], id);
            update_child(node_idx, nodes[node_idx].nxt[c], id);
        }
    }
    
    void add(const string& str, int id) { add(str, 0, 0, id); }

    void add(const string& str) { add(str, nodes[0].exist); }

    void add_solve(const string& str, vector<vector<int>>& table, int str_idx, int node_idx, int id){
        if(str_idx == str.size())   update_direct(node_idx, id);
        else{
            for(int i = 0; i < 26; ++i){
                if(nodes[node_idx].nxt[i] == -1)    continue;
                int pos = *lower_bound(table[i].begin(), table[i].end(), str_idx);
                if(pos == str.size())   continue;
                if(nodes[nodes[node_idx].nxt[i]].accept.size()) ++ans;
            }
            const int c = str[str_idx] - margin;
            if(nodes[node_idx].nxt[c] == -1){
                nodes[node_idx].nxt[c] = (int)nodes.size();
                nodes.push_back(Node());
            }
            add_solve(str, table, str_idx + 1, nodes[node_idx].nxt[c], id);
            update_child(node_idx, nodes[node_idx].nxt[c], id);
        }
    }

    void add_solve(const string& str, vector<vector<int>>& table, int id)   { add_solve(str, table, 0, 0, id); }

    void add_solve(const string& str, vector<vector<int>>& table)   { add_solve(str, table, (int)nodes[0].exist); }

    int count() const { return nodes[0].exist; }

    int size() const { return (int)nodes.size(); }
};

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    cin >> n;
    vector<string> s(n);
    for(int i = 0; i < n; ++i){
        cin >> s[i];
        reverse(s[i].begin(), s[i].end());
    }
    sort(s.begin(), s.end(), [](const string& s1, const string& s2){
        if(s1.length() == s2.length())  return s1 < s2;
        else    return s1.length() < s2.length();
    });
    vector<vector<vector<int>>> table(n, vector<vector<int>>(26));
    for(int i = 0; i < n; ++i){
        int len = s[i].length();
        for(int j = 0; j < len; ++j){
            table[i][s[i][j] - 'a'].emplace_back(j);
        }
        for(int j = 0; j < 26; ++j) table[i][j].emplace_back(len);
    }
    Trie<26, 'a'> trie;
    for(int i = 0; i < n; ++i)  trie.add_solve(s[i], table[i]);
    cout << trie.ans << endl;
}

Measured memory_bytes: 388420