#include #include #include #include #define rep(i,a,b) for(int i=(a);i<(b);i++) #define rrep(i,a,b) for(int i=(b)-1;i>=(a);i--) #define all(x) begin(x),end(x) #define sz(x) (int)(x).size() using namespace std; using namespace atcoder; using ll=long long; using ld=long double; using vll=vector; using vvll=vector; using vi=vector; using vvi=vector; using pll=pair; using pi=pair; // using mint=modint998244353; // using BoostPoint=boost::polygon::point_data; // using Voronoi=boost::polygon::voronoi_diagram; #line 2 "string/trie.hpp" template struct Trie { struct Node { array nxt; vector idxs; int idx; char key; Node(char c) : idx(-1), key(c) { fill(nxt.begin(), nxt.end(), -1); } }; vector st; Trie(char c = '$') { st.emplace_back(c); } inline int &next(int i, int j) { return st[i].nxt[j]; } void add(const string &s, int x) { int pos = 0; for (int i = 0; i < (int)s.size(); i++) { int k = s[i] - margin; if (~next(pos, k)) { pos = next(pos, k); continue; } int npos = st.size(); next(pos, k) = npos; st.emplace_back(s[i]); pos = npos; } st[pos].idx = x; st[pos].idxs.emplace_back(x); } int find(const string &s) { int pos = 0; for (int i = 0; i < (int)s.size(); i++) { int k = s[i] - margin; if (next(pos, k) < 0) return -1; pos = next(pos, k); } return pos; } int move(int pos, char c) { assert(pos < (int)st.size()); return pos < 0 ? -1 : next(pos, c - margin); } int size() const { return st.size(); } int idx(int pos) { return pos < 0 ? -1 : st[pos].idx; } vector idxs(int pos) { return pos < 0 ? vector() : st[pos].idxs; } }; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int N; cin>>N; Trie<26,'a'>tr; vi last(1,-1),cnt(1,0); ll ans=0; rep(i,0,N){ string s; cin>>s; tr.add(s,i); last.resize(tr.size(),-1),cnt.resize(tr.size(),0); int pos=0; for(auto c:s){ pos=tr.move(pos,c); if(last[pos]==i-1)cnt[pos]++; else cnt[pos]=1; last[pos]=i; ll t=cnt[pos]; ans+=t*(t+1)/2; } } cout<