#include #include #include #include #include #include #include #include using namespace std; const int MAX_LEN = 1000000; const int MAXN = 1000000; struct Trie { vector< unordered_map > next; vector cnt; int nodes; void clear() { nodes = 0; int root = newNode(); } int newNode() { if (nodes == next.size()) { next.push_back(unordered_map()); cnt.push_back(0); } else { next[nodes].clear(); cnt[nodes] = 0; } return nodes ++; } void insert(const string &s) { int u = 0; for (int i = 0; i < s.size(); ++ i) { int c = s[i] - 'a'; if (!next[u].count(c)) { next[u][c] = newNode(); } u = next[u][c]; } ++ cnt[u]; } }trie; char temp[MAX_LEN + 1]; string words[MAXN]; int n; unsigned long long MAGIC = 0xabcdef; unsigned long long prefix[MAX_LEN + 1], suffix[MAX_LEN + 1], pw[MAX_LEN + 1]; unsigned long long getHash(unsigned long long s[], int l, int r) { return s[r + 1] - s[l] * pw[r - l + 1]; } // AB is a Palindrome // Suppose |A| < |B| // Insert all A's in a trie // Traverse the reverse of B on the trie, accumulate the answer when the prefix of B is a palindrome long long solve() { trie.clear(); for (int i = 0; i < n; ++ i) { trie.insert(words[i]); } long long answer = 0; for (int i = 0; i < n; ++ i) { int len = words[i].size(); prefix[0] = 0; suffix[0] = 0; for (int j = 0; j < len; ++ j) { prefix[j + 1] = prefix[j] * MAGIC + words[i][j]; suffix[j + 1] = suffix[j] * MAGIC + words[i][len - 1 - j]; } int u = 0; for (int j = len - 1; j >= 1; -- j) { int c = words[i][j] - 'a'; if (!trie.next[u].count(c)) { break; } u = trie.next[u][c]; if (getHash(prefix, 0, j - 1) == getHash(suffix, len - j, len - 1)) { answer += trie.cnt[u]; } } } return answer; } int main() { pw[0] = 1; for (int i = 1; i <= MAX_LEN; ++ i) { pw[i] = pw[i - 1] * MAGIC; } int tests; for (assert(scanf("%d", &tests) == 1 && 1 <= tests && tests <= 5); tests --; ) { assert(scanf("%d", &n) == 1 && 1 <= n && n <= MAXN); unordered_map h; long long answer = 0; int length = 0; for (int i = 0; i < n; ++ i) { assert(scanf("%s", temp) == 1); int len = strlen(temp); assert(len <= MAX_LEN); for (int j = 0; j < len; ++ j) { assert('a' <= temp[j] && temp[j] <= 'z'); } length += len; assert(length <= MAX_LEN); words[i] = temp; // deal with strings with the same length, i.e., |A| = |B| string rev = words[i]; reverse(rev.begin(), rev.end()); if (h.count(rev)) { answer += h[rev] * 2; } h[words[i]] += 1; } // solve |A| < |B| answer += solve(); // solve |B| < |A| for (int i = 0; i < n; ++ i) { reverse(words[i].begin(), words[i].end()); } answer += solve(); printf("%lld\n", answer); } return 0; }