Educational Codeforces Round 159 (Rated for Div. 2)

E. Collapsing Strings

https://codeforces.com/contest/1902/problem/E

2 seconds / 256 megabytes

standard input / standard output

Problem

You are given n strings s1,s2,,sn, consisting of lowercase Latin letters. Let |x| be the length of string x.

Let a collapse C(a,b) of two strings a and b be the following operation:

  • if a is empty, C(a,b)=b;
  • if b is empty, C(a,b)=a;
  • if the last letter of a is equal to the first letter of b, then C(a,b)=C(a1,|a|1,b2,|b|), where sl,r is the substring of s from the l-th letter to the r-th one;
  • otherwise, C(a,b)=a+b, i. e. the concatenation of two strings.

Calculate i=1nj=1n|C(si,sj)|.

Input

The first line contains a single integer n (1n106).

Each of the next n lines contains a string si (1|si|106), consisting of lowercase Latin letters.

The total length of the strings doesn't exceed 106.

Output

Print a single integer — i=1nj=1n|C(si,sj)|.

Examples

Input

3
aba
ab
ba

Output

20

Input

5
abab
babx
xab
xba
bab

Output

126

题解

注意到,C(a,b) 去除的部分实际上就是 a 的逆序串 ab 的最长公共前缀。如:

a=abdcca=ccdbab=ccdaaC(a,b)=abaa

我们将最长公共前缀记为 lcp(),那么可以用公式表示:

C(a,b)=|a|+|b|2|lcp(a,b)|

根据上面的式子,将问题变形:

i=1nj=1n|C(si,sj)|=2ni=1n|si|i=1nj=1nlcp(si,sj)

下面的重点就是求出 i=1nj=1nlcp(si,sj) 了。要求每一种组合的最长公共前缀,进行双循环的话,很明显复杂度不能接受。不过我们可以换一种思维方式,考虑每一种前缀对答案的贡献。对于 si 中的每一种前缀,如果也符合 sj 的前缀,那么答案就应该减去这种符合这种前缀的字符串数目。

字典树 (Trie 树) 的原理就是将相同前缀的字符串压缩到一起储存,因此可以用它解决这个问题。但是直接使用是不行的,因为字典树维护的是完整字符串的数量,而不是字符串的前缀的数量。

不过这个问题我们简单修改字典树的代码即可,注意下面代码的增删之处(使用 ++-- 标记):

  void insert(string &s)
  {
      int p = 0;
      for (int i = 0; i < s.size(); i++)
      {
          int c = s[i] - 'a';
          if (!son[p][c])
              son[p][c] = ++idx;
          p = son[p][c];
++        cnt[p]++;
      }
--    cnt[p]++;
  }

另外,由于我们减去的是字符串的每种前缀的数量,因此查询的代码也得进行修改:

  int query(string &s)
  {
--    int p = 0;
++    int p = 0, res = 0;
      for (int i = 0; i < s.size(); i++)
      {
          int c = s[i] - 'a';
          if (!son[p][c])
--            return 0;
++            return res;
          p = son[p][c];
++        res += cnt[p];
      }
--    return cnt[p];
++    return res;
  }

代码

#include <bits/stdc++.h>
#define endl '\n'
#define int long long

using namespace std;

const int MAXN = 1e6 + 10;
int son[MAXN][26], cnt[MAXN], idx;

void insert(string &s)
{
    int p = 0;
    for (int i = 0; i < s.size(); i++)
    {
        int c = s[i] - 'a';
        if (!son[p][c])
            son[p][c] = ++idx;
        p = son[p][c];
        cnt[p]++;
    }
}

int query(string &s)
{
    int p = 0, res = 0;
    for (int i = 0; i < s.size(); i++)
    {
        int c = s[i] - 'a';
        if (!son[p][c])
            return res;
        p = son[p][c];
        res += cnt[p];
    }
    return res;
}

void solve()
{
    int n;
    cin >> n;
    vector<string> s(n);
    int ans = 0;
    for (int i = 0; i < n; i++)
    {
        cin >> s[i];
        ans += s[i].size();
        insert(s[i]);
    }
    ans *= 2 * n;
    for (int i = 0; i < n; i++)
    {
        reverse(s[i].begin(), s[i].end());
        ans -= 2 * query(s[i]);
    }
    cout << ans << endl;
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    // cin >> t;
    while (t--)
        solve();
    return 0;
}