Problem:
C: Cyclic String
問題文
文字列 $S = s_0 s_1 \ldots s_{n-1}$ が文字列 $T = t_0 t_1 \ldots t_{m-1}$ に巡回的に含まれるとは、
ある $k$ があって、全ての $i$ $(0 \le i \lt n)$ について
$s_i = t_{(i+k) \bmod m}$ が成立する事を言います。
$N$ 個の文字列 $S_1, \dots, S_N$ が与えられます。
以下の条件を満たす文字列の集合 $\{C_1, \dots, C_k\}$ を考えます（$k$ は任意）:
各$i$ $(1 \leq i \leq N)$について、$S_i$は少なくとも1つの$C_j$に巡回的に含まれる。$(1 \leq j \leq k)$
$|C_1| + |C_2| + \cdots + |C_k|$の最小値を求めてください。
制約
$1 \leq N \leq 100$
$1 \leq |S_i| \leq 1000$
$S_i$ の各文字は
英小文字
または
英大文字
である
任意の$i,j$ $(i \ne j)$について、$S_i$ は $S_j$ の連続する部分文字列ではない
入力
入力は標準入力から以下の形式で与えられます。
$N$
$S_1$
$S_2$
$\vdots$
$S_N$
出力
答えを一行で出力してください。
入出力例
入力例1
3
dea
xxx
abcd
出力例1
6
例えば、文字列の集合 {"abcde", "x"} は問題文の条件を満たし、文字列の長さの合計は $6$ です。
入力例2
1
xy
出力例2
2
入力例3
3
AAaaBB
aaBBAA
AaB
出力例3
9

Submitted solutions:

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int rh_mod = 1e9 + 7;
const int rh_b1 = 1009, rh_b2 = 10007;

class rolling_hash {
	const int n;
	vector<int> r1, r2, h1, h2;
public:
	rolling_hash(const string& s)
		: n(s.size()), r1(n + 1), r2(n + 1), h1(n + 1), h2(n + 1) {
		r1[0] = r2[0] = 1;
		for (int i = 0; i < n; i++) {
			r1[i + 1] = (ll)r1[i] * rh_b1 % rh_mod;
			r2[i + 1] = (ll)r2[i] * rh_b2 % rh_mod;
			h1[i + 1] = ((ll)h1[i] * rh_b1 + s[i]) % rh_mod;
			h2[i + 1] = ((ll)h2[i] * rh_b2 + s[i]) % rh_mod;
		}
	}
	ll get(int l, int r) {
		assert(0 <= l && l <= r && r <= n);
		ll x1 = (h1[r] - (ll)h1[l] * r1[r - l] % rh_mod + rh_mod) % rh_mod;
		ll x2 = (h2[r] - (ll)h2[l] * r2[r - l] % rh_mod + rh_mod) % rh_mod;
		return (x1 << 32LL) | x2;
	}
};

const int INF = 1e9;

class min_cost_flow {
	using pii = pair<int, int>;
	struct edge {
		int to, cap, cost, rev;
		edge(int to_, int cap_, int cost_, int rev_)
			: to(to_), cap(cap_), cost(cost_), rev(rev_) {}
	};
	int V;
	vector<vector<edge>> G;
	vector<int> h, dist, prevv, preve;
public:
	min_cost_flow(int _V) : V(_V), G(_V), h(_V), dist(_V), prevv(_V), preve(_V) {}
	void add(int from, int to, int cap, int cost) {
		G[from].push_back(edge(to, cap, cost, G[to].size()));
		G[to].push_back(edge(from, 0, -cost, G[from].size() - 1));
	}
	int calc(int s, int t, int f) {
		int res = 0;
		fill(h.begin(), h.end(), 0);
		while (f > 0) {
			priority_queue<pii, vector<pii>, greater<pii>> que;
			fill(dist.begin(), dist.end(), INF);
			dist[s] = 0;
			que.push(pii(0, s));
			while (!que.empty()) {
				pii p = que.top(); que.pop();
				int v = p.second;
				if (dist[v] < p.first) continue;
				for (size_t i = 0; i < G[v].size(); i++) {
					edge &e = G[v][i];
					if (e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]) {
						dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
						prevv[e.to] = v;
						preve[e.to] = i;
						que.push(pii(dist[e.to], e.to));
					}
				}
			}
			if (dist[t] == INF) return -1;
			for (int v = 0; v < V; v++)
				h[v] += dist[v];
			int d = f;
			for (int v = t; v != s; v = prevv[v]) {
				d = min(d, G[prevv[v]][preve[v]].cap);
			}
			f -= d;
			res += d * h[t];
			for (int v = t; v != s; v = prevv[v]) {
				edge &e = G[prevv[v]][preve[v]];
				e.cap -= d;
				G[v][e.rev].cap += d;
			}
		}
		return res;
	}
};

const int inf = 1e6;

int main()
{
	int N;
	cin >> N;
	vector<string> S(N);
	for (int i = 0; i < N; i++) {
		cin >> S[i];
	}
	sort(S.begin(), S.end());
	S.erase(unique(S.begin(), S.end()), S.end());
	N = S.size();
	vector<rolling_hash> rh;
	for (int i = 0; i < N; i++) {
		rh.emplace_back(S[i]);
	}
	vector<bool> er(N);
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) if (i != j && S[i].size() <= S[j].size()) {
			for (int p = 0; p + (int)S[i].size() <= (int)S[j].size(); p++) {
				if (rh[i].get(0, S[i].size()) == rh[j].get(p, p + S[i].size())) {
					er[i] = true;
					break;
				}
			}
		}
	}
	{
		auto tmp = S;
		S.clear();
		for (int i = 0; i < N; i++) if (!er[i]) S.push_back(tmp[i]);
		N = S.size();
	}
	rh.clear();
	for (int i = 0; i < N; i++) {
		rh.emplace_back(S[i]);
	}
	int sum = 0;
	for (int i = 0; i < N; i++) sum += S[i].size();
	min_cost_flow mcf(N * 2 + 2);
	int src = N * 2, snk = N * 2 + 1;
	for (int i = 0; i < N; i++) mcf.add(src, i, 1, 0);
	for (int i = 0; i < N; i++) mcf.add(N + i, snk, 1, 0);
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			int sz = min(S[i].size(), S[j].size()), c = 0;
			for (int k = 0; k < sz; k++) {
				if (rh[i].get(S[i].size() - k, S[i].size()) == rh[j].get(0, k)) {
					c = k;
				}
			}
			mcf.add(i, N + j, 1, inf - c);
		}
	}
	cout << sum - (inf * N - mcf.calc(src, snk, N)) << endl;
	return 0;
}
accuracy: 1
time_ms: 90
memory_kb: 5604
final_rank: 12

code_snippet: // AOJ #2980 Cyclic String
// 2025.3.26

#include <bits/stdc++.h>
using namespace std;

#define gc() getchar_unlocked()
#define pc(c) putchar_unlocked(c)

int Cin() {  // 整数の入力
	int n = 0; int c = gc();
	if (c == '-') {	c = gc();
		do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
		return -n;
	}
	do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
	return n;
}

char temp[1005];
string Cins() { // 文字列の入力　スペース以下の文字で入力終了
    char *s = temp;
	do *s = gc();
	while (*s++ > ' ');
	*(s-1) = 0;
    string input(temp); // 文字配列からstringを作成
    return input;
}

void Cout(int n) { // 整数の表示（出力）
	char b[30];
	if (!n) pc('0');
	else {
		if (n < 0) pc('-'), n = -n;
		int i = 0; while (n) b[i++] = n % 10 + '0', n /= 10;
		while (i--) pc(b[i]);
	}
	pc('\n');
}

const int INF = INT_MAX;

vector<int> kmp(const string &s) {
    int n = s.size();
    vector<int> pi(n + 1);
    pi[0] = -1;
    for (int i = 1; i <= n; i++) {
        int j = pi[i - 1];
        while (j >= 0 && s[j] != s[i - 1]) j = pi[j];
        pi[i] = j + 1;
    }
    return pi;
}

template <class Edge, class Cost>
struct Hungarian {
    int n, m;
    const vector<vector<Edge>> &g;
    vector<Cost> l, r;
    vector<int> to, ot;
    vector<int> par;
    vector<Cost> minval;
    vector<char> usedL, usedR;
    int sizeMatch = 0;

    void update(int i) {
        usedL[i] = true;
        for (const auto &e : g[i]) {
            if (!usedR[e.to]) {
                Cost cur = l[i] + r[e.to] - e.cost;
                if (minval[e.to] > cur) {
                    minval[e.to] = cur;
                    par[e.to] = i;
                }
            }
        }
    }

    bool augment() {
        fill(par.begin(), par.end(), -1);
        fill(minval.begin(), minval.end(), INF);
        fill(usedL.begin(), usedL.end(), false);
        fill(usedR.begin(), usedR.end(), false);
        for (int i = 0; i < n; i++) if (to[i] == -1) update(i);

        while (true) {
            int j = -1;
            for (int k = 0; k < m; k++) {
                if (!usedR[k] && par[k] != -1 && (j == -1 || minval[k] < minval[j])) j = k;
            }
            if (j == -1) return false;
            Cost delta = minval[j];
            for (int i = 0; i < n; i++) if (usedL[i]) l[i] -= delta;
            for (int k = 0; k < m; k++) {
                if (usedR[k]) r[k] += delta;
                else if (par[k] != -1) minval[k] -= delta;
            }
            usedR[j] = true;
            if (ot[j] == -1) {
                while (j != -1) {
                    int i = par[j];
                    int nxt = to[i];
                    to[i] = j;
                    ot[j] = i;
                    j = nxt;
                }
                sizeMatch++;
                return true;
            } else update(ot[j]);
        }
    }

    Hungarian(int nn, int mm, const vector<vector<Edge>> &gg)
        : n(nn), m(mm), g(gg),
          l(nn), r(mm), to(nn, -1), ot(mm, -1),
          par(mm), minval(mm),
          usedL(nn), usedR(mm)
    {
        Cost mx = -INF;
        for (int i = 0; i < n; i++)
            for (const auto &e : g[i]) mx = max(mx, e.cost);
        fill(l.begin(), l.end(), mx);
        while (augment());
    }

    Cost get() {
        Cost mn = *min_element(l.begin(), l.end());
        Cost sumL = accumulate(l.begin(), l.end(), static_cast<Cost>(0));
        Cost sumR = accumulate(r.begin(), r.end(), static_cast<Cost>(0));
        return sumL + sumR + (-n + sizeMatch) * mn;
    }
};

struct Edge { int to, cost; };

int main() {
    int n = Cin();
    vector<string> s(n);
    for (int i = 0; i < n; i++) s[i] = Cins();

    vector<vector<int>> kp(n);
    for (int i = 0; i < n; i++) kp[i] = kmp(s[i]);

    vector<vector<Edge>> g(n);
    for (int i = 0; i < n; i++) {
        int len = s[i].size();
        g[i].push_back({i, kp[i][len]});
        for (int j = 0; j < n; j++) {
            if (i == j) continue;
            int x = 0;
            for (char c : s[i]) {
                while (x >= 0 && s[j][x] != c) x = kp[j][x];
                x++;
            }
            g[i].push_back({j, x});
        }
    }

    Hungarian<Edge, int> hung(n, n, g);
    int ans = -hung.get();
    for (const auto &str : s) ans += str.size();
    Cout(ans);
    return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 3940
final_rank: 5

code_snippet: // AOJ #2980 Cyclic String
// 2025.3.26

#include <bits/stdc++.h>
using namespace std;

const int INF = INT_MAX;

vector<int> kmp(const string &s) {
    int n = s.size();
    vector<int> pi(n + 1);
    pi[0] = -1;
    for (int i = 1; i <= n; i++) {
        int j = pi[i - 1];
        while (j >= 0 && s[j] != s[i - 1]) j = pi[j];
        pi[i] = j + 1;
    }
    return pi;
}

template <class Edge, class Cost>
struct Hungarian {
    int n, m;
    const vector<vector<Edge>> &g;
    vector<Cost> l, r;
    vector<int> to, ot;
    vector<int> par;
    vector<Cost> minval;
    vector<char> usedL, usedR;
    int sizeMatch = 0;

    void update(int i) {
        usedL[i] = true;
        for (const auto &e : g[i]) {
            if (!usedR[e.to]) {
                Cost cur = l[i] + r[e.to] - e.cost;
                if (minval[e.to] > cur) {
                    minval[e.to] = cur;
                    par[e.to] = i;
                }
            }
        }
    }

    bool augment() {
        fill(par.begin(), par.end(), -1);
        fill(minval.begin(), minval.end(), INF);
        fill(usedL.begin(), usedL.end(), false);
        fill(usedR.begin(), usedR.end(), false);
        for (int i = 0; i < n; i++) if (to[i] == -1) update(i);

        while (true) {
            int j = -1;
            for (int k = 0; k < m; k++) {
                if (!usedR[k] && par[k] != -1 && (j == -1 || minval[k] < minval[j])) j = k;
            }
            if (j == -1) return false;
            Cost delta = minval[j];
            for (int i = 0; i < n; i++) if (usedL[i]) l[i] -= delta;
            for (int k = 0; k < m; k++) {
                if (usedR[k]) r[k] += delta;
                else if (par[k] != -1) minval[k] -= delta;
            }
            usedR[j] = true;
            if (ot[j] == -1) {
                while (j != -1) {
                    int i = par[j];
                    int nxt = to[i];
                    to[i] = j;
                    ot[j] = i;
                    j = nxt;
                }
                sizeMatch++;
                return true;
            } else update(ot[j]);
        }
    }

    Hungarian(int nn, int mm, const vector<vector<Edge>> &gg)
        : n(nn), m(mm), g(gg),
          l(nn), r(mm), to(nn, -1), ot(mm, -1),
          par(mm), minval(mm),
          usedL(nn), usedR(mm)
    {
        Cost mx = -INF;
        for (int i = 0; i < n; i++)
            for (const auto &e : g[i]) mx = max(mx, e.cost);
        fill(l.begin(), l.end(), mx);
        while (augment());
    }

    Cost get() {
        Cost mn = *min_element(l.begin(), l.end());
        Cost sumL = accumulate(l.begin(), l.end(), static_cast<Cost>(0));
        Cost sumR = accumulate(r.begin(), r.end(), static_cast<Cost>(0));
        return sumL + sumR + (-n + sizeMatch) * mn;
    }
};

struct Edge { int to, cost; };

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

    int n;
    cin >> n;
    vector<string> s(n);
    for (int i = 0; i < n; i++) cin >> s[i];

    vector<vector<int>> kp(n);
    for (int i = 0; i < n; i++) kp[i] = kmp(s[i]);

    vector<vector<Edge>> g(n);
    for (int i = 0; i < n; i++) {
        int len = s[i].size();
        g[i].push_back({i, kp[i][len]});
        for (int j = 0; j < n; j++) {
            if (i == j) continue;
            int x = 0;
            for (char c : s[i]) {
                while (x >= 0 && s[j][x] != c) x = kp[j][x];
                x++;
            }
            g[i].push_back({j, x});
        }
    }

    Hungarian<Edge, int> hung(n, n, g);
    int ans = -hung.get();
    for (const auto &str : s) ans += str.size();
    cout << ans << endl;
    return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 4084
final_rank: 6

code_snippet: // AOJ #2980 Cyclic String
// 2025.2.27

#include <bits/stdc++.h>
using namespace std;

vector<int> prefix(const string &s) {
    int n = s.size();
    vector<int> pi(n,0);
    for(int i = 1; i < n; i++){
        int j = pi[i-1];
        while(j > 0 && s[i] != s[j]) j = pi[j-1];
        if(s[i] == s[j]) j++;
        pi[i] = j;
    }
    return pi;
}

int minimalPeriod(const string &s){
    int n = s.size();
    vector<int> pi = prefix(s);
    int k = n - pi[n-1];
    if(n % k == 0) return k;
    return n;
}

int overlap(const string &a, const string &b) {
    int la = a.size(), lb = b.size();
    int maxK = min(la, lb);
    for (int k = maxK; k >= 0; k--) {
        if(k == 0) return 0;
        if(a.compare(la - k, k, b, 0, k) == 0) return k;
    }
    return 0;
}

int hungarian(const vector<vector<int>>& cost, int n, vector<int>& matchR) {
    const int INF = 1e9;
    vector<int> u(n+1,0), v(n+1,0), p(n+1,0), way(n+1,0);
    for (int i = 1; i <= n; i++) {
        p[0] = i;
        vector<int> minv(n+1, INF);
        vector<char> used(n+1, false);
        int j0 = 0;
        do {
            used[j0] = true;
            int i0 = p[j0], delta = INF, j1 = 0;
            for (int j = 1; j <= n; j++) {
                if (!used[j]) {
                    int cur = cost[i0-1][j-1] - u[i0] - v[j];
                    if(cur < minv[j]){
                        minv[j] = cur;
                        way[j] = j0;
                    }
                    if(minv[j] < delta){
                        delta = minv[j];
                        j1 = j;
                    }
                }
            }
            for (int j = 0; j <= n; j++) {
                if(used[j]){
                    u[p[j]] += delta;
                    v[j] -= delta;
                } else {
                    minv[j] -= delta;
                }
            }
            j0 = j1;
        } while(p[j0] != 0);

        do {
            int j1 = way[j0];
            p[j0] = p[j1];
            j0 = j1;
        } while(j0);
    }
    matchR.assign(n, -1);
    for (int j = 1; j <= n; j++){
        matchR[p[j]-1] = j-1;
    }
    return -v[0];
}

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

    int N;
    cin >> N;
    vector<string> S(N);
    for (int i = 0; i < N; i++) cin >> S[i];

    int ans = 0;
    vector<int> minPeriod(N, 0);
    for (int i = 0; i < N; i++){
        ans += S[i].size();
        minPeriod[i] = minimalPeriod(S[i]);
    }

    vector<vector<int>> weight(N, vector<int>(N, 0));
    for (int i = 0; i < N; i++){
        for (int j = 0; j < N; j++){
            if(i == j) weight[i][j] = S[i].size() - minPeriod[i];
            else weight[i][j] = overlap(S[i], S[j]);
        }
    }

    int maxW = 0;
    for (int i = 0; i < N; i++){
        for (int j = 0; j < N; j++) maxW = max(maxW, weight[i][j]);
    }
    vector<vector<int>> cost(N, vector<int>(N, 0));
    for (int i = 0; i < N; i++){
        for (int j = 0; j < N; j++) cost[i][j] = maxW - weight[i][j];
    }

    vector<int> matchR;
    cout << ans - (N * maxW - hungarian(cost, N, matchR)) << endl;
    return 0;
}
accuracy: 0.40476190476190477
time_ms: 40
memory_kb: 3568
final_rank: 19

code_snippet: #include <bits/stdc++.h>
using namespace std;

#define ll long long
#define elif else if
#define vi vector<int>
#define vll vector<ll>
#define vvi vector<vi>
#define rep(i, n) for (int i = 0; i < (n); i++)
#define rep2(i, l, r) for (int i = l; i < (r); i++)

template <typename T>
pair<T, vector<int>> hungarian(vector<vector<T>> &A)
{
  const T infty = numeric_limits<T>::max();
  const int N = (int)A.size();
  const int M = (int)A[0].size();
  vector<int> P(M), way(M);
  vector<T> U(N, 0), V(M, 0), minV;
  vector<bool> used;

  for (int i = 1; i < N; i++)
  {
    P[0] = i;
    minV.assign(M, infty);
    used.assign(M, false);
    int j0 = 0;
    while (P[j0] != 0)
    {
      int i0 = P[j0], j1 = 0;
      used[j0] = true;
      T delta = infty;
      for (int j = 1; j < M; j++)
      {
        if (used[j])
          continue;
        T curr = A[i0][j] - U[i0] - V[j];
        if (curr < minV[j])
          minV[j] = curr, way[j] = j0;
        if (minV[j] < delta)
          delta = minV[j], j1 = j;
      }
      for (int j = 0; j < M; j++)
      {
        if (used[j])
          U[P[j]] += delta, V[j] -= delta;
        else
          minV[j] -= delta;
      }
      j0 = j1;
    }
    do
    {
      P[j0] = P[way[j0]];
      j0 = way[j0];
    } while (j0 != 0);
  }
  return {-V[0], P};
}

vector<int> z_algorithm(const string &s)
{
  vector<int> prefix(s.size());
  for (int i = 1, j = 0; i < s.size(); i++)
  {
    if (i + prefix[i - j] < j + prefix[j])
    {
      prefix[i] = prefix[i - j];
    }
    else
    {
      int k = max(0, j + prefix[j] - i);
      while (i + k < s.size() && s[k] == s[i + k])
        ++k;
      prefix[i] = k;
      j = i;
    }
  }
  prefix[0] = (int)s.size();
  return prefix;
}

int calc(string &S, string &T)
{
  vi z = z_algorithm(T + S);
  int nS = S.size();
  int nT = T.size();
  rep2(i, 1, nS)
  {
    if (z[nT + i] == nS - i)
      return nS - i;
  }
  return 0;
}

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

  int N;
  cin >> N;
  vector<string> S(N);
  rep(i, N) cin >> S[i];
  vvi C(N + 1, vi(N + 1, 0));
  rep(i, N) rep(j, N) C[i + 1][j + 1] = -calc(S[i], S[j]);
  int ans = 0;
  rep(i, N) ans += S[i].size();
  cout << ans + hungarian(C).first << endl;
}
accuracy: 1
time_ms: 40
memory_kb: 3552
final_rank: 1

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b) for (int i = (int)(a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b)-1; i >= (int)(a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())

using uint = unsigned int;
using ll = long long int;
using ull = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
const int inf = 0x3fffffff;
const ll INF = 0x1fffffffffffffff;

template <typename T> inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T> inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T, typename U> T ceil(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <typename T, typename U> T floor(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? x / y : (x - y + 1) / y);
}
template <typename T> int popcnt(T x) {
    return __builtin_popcountll(x);
}
template <typename T> int topbit(T x) {
    return (x == 0 ? -1 : 63 - __builtin_clzll(x));
}
template <typename T> int lowbit(T x) {
    return (x == 0 ? -1 : __builtin_ctzll(x));
}

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << "P(" << p.first << ", " << p.second << ")";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "{";
    for (int i = 0; i < vec.size(); i++) {
        os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
    }
    os << "}";
    return os;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, const map<T, U> &map_var) {
    os << "{";
    for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
        os << "(" << itr->first << ", " << itr->second << ")";
        itr++;
        if (itr != map_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const set<T> &set_var) {
    os << "{";
    for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
        os << *itr;
        ++itr;
        if (itr != set_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
#ifdef LOCAL
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define show(...) true
#endif
template <typename T> void _show(int i, T name) {
    cerr << '\n';
}
template <typename T1, typename T2, typename... T3>
void _show(int i, const T1 &a, const T2 &b, const T3 &...c) {
    for (; a[i] != ',' && a[i] != '\0'; i++)
        cerr << a[i];
    cerr << ":" << b << " ";
    _show(i + 1, a, c...);
}

/**
 * @brief template
 */

namespace Random {
mt19937_64 randgen(chrono::steady_clock::now().time_since_epoch().count());
using u64 = unsigned long long;
u64 get() {
    return randgen();
}
template <typename T> T get(T L) { // [0,L]

    return get() % (L + 1);
}
template <typename T> T get(T L, T R) { // [L,R]

    return get(R - L) + L;
}
}; // namespace Random

/**
 * @brief Random
 */

struct RollingHash {
    using ull = unsigned long long;
    const ull MOD = 0x1fffffffffffffff;
    const ull base;
    vector<ull> hashed, power;

    static constexpr ull mask(ll a) { return (1ULL << a) - 1; }

    inline ull mul(ull a, ull b) const {
        __uint128_t ans = __uint128_t(a) * b;
        ans = (ans >> 61) + (ans & MOD);
        if (ans >= MOD)
            ans -= MOD;
        return ans;
    }

    static inline ull genbase() { return Random::get(ull(0x1fffffffffffffff)); }
    RollingHash() = default;

    RollingHash(const string &s, ull base) : base(base) {
        ll n = s.size();
        hashed.assign(n + 1, 0);
        power.assign(n + 1, 0);
        power[0] = 1;
        for (ll i = 0; i < n; i++) {
            power[i + 1] = mul(power[i], base);
            hashed[i + 1] = mul(hashed[i], base) + s[i];
            if (hashed[i + 1] >= MOD)
                hashed[i + 1] -= MOD;
        }
    }

    ull get(ll l, ll r) const {
        ull ret = hashed[r] + MOD - mul(hashed[l], power[r - l]);
        if (ret >= MOD)
            ret -= MOD;
        return ret;
    }

    ull connect(ull h1, ull h2, ll h2len) const {
        ull ret = mul(h1, power[h2len]) + h2;
        if (ret >= MOD)
            ret -= MOD;
        return ret;
    }

    void connect(const string &s) {
        ll n = hashed.size() - 1, m = s.size();
        hashed.resize(n + m + 1);
        power.resize(n + m + 1);
        for (ll i = n; i < n + m; i++) {
            power[i + 1] = mul(power[i], base);
            hashed[i + 1] = mul(hashed[i], base) + s[i - n];
            if (hashed[i + 1] >= MOD)
                hashed[i + 1] -= MOD;
        }
    }

    ll LCP(const RollingHash &b, ll l1, ll r1, ll l2, ll r2) {
        ll len = min(r1 - l1, r2 - l2);
        ll low = -1, high = len + 1;
        while (high - low > 1) {
            ll mid = (low + high) / 2;
            if (get(l1, l1 + mid) == b.get(l2, l2 + mid))
                low = mid;
            else
                high = mid;
        }
        return low;
    }
};

/**
 * @brief Rolling Hash
 */

struct MaxFlow {
    struct Edge {
        int to, rev;
        ll cap;
    };
    int V;
    vector<vector<Edge>> G;
    vector<int> itr, level;
    using P = pair<int, int>;
    vector<P> es;

  public:
    MaxFlow() {}
    MaxFlow(int V) : V(V) {
        G.assign(V, vector<Edge>());
    }
    int add_vertex() {
        G.push_back(vector<Edge>());
        return V++;
    }
    void add_edge(int from, int to, ll cap) {
        int fid = SZ(G[from]), tid = SZ(G[to]);
        if (from == to)
            tid++;
        es.push_back({from, fid});
        G[from].push_back({to, tid, cap});
        G[to].push_back({from, fid, 0});
    }
    struct Type {
        int from, to;
        ll cap, recap;
    };
    Type get_edge(int i) {
        auto [from, pos] = es[i];
        auto e = G[from][pos];
        auto re = G[e.to][e.rev];
        return Type{from, e.to, e.cap, re.cap};
    }
    void bfs(int s) {
        level.assign(V, -1);
        queue<int> q;
        level[s] = 0;
        q.push(s);
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            for (auto &e : G[v]) {
                if (e.cap > 0 && level[e.to] < 0) {
                    level[e.to] = level[v] + 1;
                    q.push(e.to);
                }
            }
        }
    }
    ll dfs(int v, int t, ll f) {
        if (v == t)
            return f;
        for (int &i = itr[v]; i < (int)G[v].size(); i++) {
            Edge &e = G[v][i];
            if (e.cap > 0 && level[v] < level[e.to]) {
                ll d = dfs(e.to, t, min(f, e.cap));
                if (d > 0) {
                    e.cap -= d, G[e.to][e.rev].cap += d;
                    return d;
                }
            }
        }
        return 0;
    }
    ll run(int s, int t) {
        ll ret = 0, f;
        while (bfs(s), level[t] >= 0) {
            itr.assign(V, 0);
            while ((f = dfs(s, t, INF)) > 0)
                ret += f;
        }
        return ret;
    }
    vector<int> cut() {
        vector<int> ret(V);
        rep(v, 0, V) if (level[v] < 0) ret[v] = 1;
        return ret;
    }
};

/**
 * @brief Maximum Flow
 */

template <class Cap, class Cost> struct MinCostFlow {
    struct X {
        int from, to;
        Cap lb, ub, flow;
        Cost cost;
    };
    struct Edge {
        int to, rev;
        Cap cap;
        Cost cost;
    };
    using P = pair<int, int>;
    int n, m;
    vector<X> es;
    vector<Cap> exc;
    vector<Cost> dual;
    vector<vector<Edge>> g;
    Cost MX;
    MinCostFlow(int _n) : n(_n), m(0), exc(n), dual(n), g(n), MX(0) {}
    void add_edge(int from, int to, Cap lb, Cap ub, Cost cost) {
        m++;
        chmax(MX, cost);
        chmax(MX, -cost);
        es.push_back({from, to, lb, ub, 0, cost});
    }
    void add_excess(int v, Cap c) { exc[v] += c; }
    pair<bool, Cost> run() {
        MaxFlow mf(n + 2);
        int S = n, T = n + 1;
        Cap psum = 0, nsum = 0;
        for (auto &e : es) {
            exc[e.to] += e.lb;
            exc[e.from] -= e.lb;
            mf.add_edge(e.from, e.to, e.ub - e.lb);
        }
        rep(i, 0, n) {
            if (exc[i] > 0) {
                psum += exc[i];
                mf.add_edge(S, i, exc[i]);
            }
            if (exc[i] < 0) {
                nsum += -exc[i];
                mf.add_edge(i, T, -exc[i]);
            }
        }

        if (psum != nsum or mf.run(S, T) != psum)
            return {false, 0};

        using P = pair<int, int>;
        vector<P> pos;
        rep(i, 0, m) {
            auto e = mf.get_edge(i);
            Cost cost = es[i].cost * n;
            int fid = SZ(g[e.from]), tid = SZ(g[e.to]);
            if (e.from == e.to)
                tid++;
            pos.push_back({e.from, fid});
            g[e.from].push_back({e.to, tid, e.cap, cost});
            g[e.to].push_back({e.from, fid, e.recap, -cost});
        }

        // solve

        Cost eps = MX * n + 1;
        while (eps > 1) {
            eps = max<Cost>(eps >> 2, 1);
            refine(eps);
        }

        Cost ret = 0;
        rep(i, 0, m) {
            auto [from, fid] = pos[i];
            es[i].flow = es[i].ub - g[from][fid].cap;
            ret += es[i].flow * es[i].cost;
        }
        dual.assign(n, 0);
        for (;;) {
            bool upd = 0;
            rep(i, 0, n) {
                for (auto &e : g[i])
                    if (e.cap) {
                        auto cost = dual[i] + e.cost / n;
                        if (chmin(dual[e.to], cost)) {
                            upd = 1;
                        }
                    }
            }
            if (!upd)
                break;
        }
        return {true, ret};
    }
    Cap get_flow(int i) const { return es[i].flow; }

  private:
    void refine(Cost &eps) {
        exc.assign(n, 0);
        vector<int> used(n);
        queue<int> que;
        vector<int> iter(n);

        auto cost = [&](int from, const Edge &e) {
            return e.cost + dual[from] - dual[e.to];
        };
        auto push = [&](int from, Edge &e, Cap cap) {
            exc[from] -= cap;
            exc[e.to] += cap;
            g[e.to][e.rev].cap += cap;
            e.cap -= cap;
        };
        auto relabel = [&](int v) {
            iter[v] = 0;
            Cost down = MX * (n + 1);
            for (auto &e : g[v])
                if (e.cap) {
                    chmin(down, eps + cost(v, e));
                }
            dual[v] -= down;
            que.push(v);
            used[v] = 1;
        };

        rep(i, 0, n) {
            for (auto &e : g[i])
                if (e.cap and cost(i, e) < 0) {
                    push(i, e, e.cap);
                }
        }
        rep(i, 0, n) if (exc[i] > 0) {
            used[i] = 1;
            que.push(i);
        }
        while (!que.empty()) {
            auto v = que.front();
            que.pop();
            used[v] = 0;
            for (int &i = iter[v]; i < SZ(g[v]); i++) {
                auto &e = g[v][i];
                if (e.cap and cost(v, e) < 0) {
                    push(v, e, min(exc[v], e.cap));
                    if (!used[e.to] and exc[e.to] > 0) {
                        used[e.to] = 1;
                        que.push(e.to);
                    }
                    if (exc[v] == 0)
                        break;
                }
            }
            if (exc[v] > 0) {
                relabel(v);
            }
        }
        eps = 0;
        rep(i, 0, n) {
            for (auto &e : g[i])
                if (e.cap) {
                    chmax(eps, -cost(i, e));
                }
        }
    }
};

/**
 * @brief Minimum Cost b-flow
 */

int main() {
    int N;
    cin >> N;
    vector<string> S(N);
    rep(i,0,N) cin >> S[i];
    ull base = RollingHash::genbase();
    vector<RollingHash> H;
    rep(i,0,N) H.push_back(RollingHash(S[i],base));
    vector<vector<int>> Cost(N,vector<int>(N,0));
    rep(i,0,N) {
        rep(j,1,S[i].size()) {
            if (H[i].get(S[i].size()-j,S[i].size()) == H[i].get(0,j)) Cost[i][i] = j;
        }
    }
    rep(i,0,N) {
        rep(j,0,N) {
            rep(k,1,min(S[i].size(),S[j].size())) {
                if (H[i].get(S[i].size()-k,S[i].size()) == H[j].get(0,k)) Cost[i][j] = k;
            }
        }
    }
    MinCostFlow<ll,ll> G(N*2);
    rep(i,0,N) {
        rep(j,0,N) {
            G.add_edge(i,j+N,0,1,-Cost[i][j]);
        }
    }
    rep(i,0,N) {
        G.add_excess(i,1LL);
        G.add_excess(i+N,-1LL);
    }
    auto [_,ans] = G.run();
    int SUM = 0;
    rep(i,0,N) SUM += S[i].size();
    cout << SUM + ans << endl;
}
accuracy: 1
time_ms: 60
memory_kb: 6572
final_rank: 13

code_snippet: #line 1 "library/Template/template.hpp"
#include <bits/stdc++.h>
using namespace std;

#define rep(i, a, b) for (int i = (int)(a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b)-1; i >= (int)(a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())

using uint = unsigned int;
using ll = long long int;
using ull = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
const int inf = 0x3fffffff;
const ll INF = 0x1fffffffffffffff;

template <typename T> inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T> inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T, typename U> T ceil(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <typename T, typename U> T floor(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? x / y : (x - y + 1) / y);
}
template <typename T> int popcnt(T x) {
    return __builtin_popcountll(x);
}
template <typename T> int topbit(T x) {
    return (x == 0 ? -1 : 63 - __builtin_clzll(x));
}
template <typename T> int lowbit(T x) {
    return (x == 0 ? -1 : __builtin_ctzll(x));
}

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << "P(" << p.first << ", " << p.second << ")";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "{";
    for (int i = 0; i < vec.size(); i++) {
        os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
    }
    os << "}";
    return os;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, const map<T, U> &map_var) {
    os << "{";
    for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
        os << "(" << itr->first << ", " << itr->second << ")";
        itr++;
        if (itr != map_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const set<T> &set_var) {
    os << "{";
    for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
        os << *itr;
        ++itr;
        if (itr != set_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
#ifdef LOCAL
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define show(...) true
#endif
template <typename T> void _show(int i, T name) {
    cerr << '\n';
}
template <typename T1, typename T2, typename... T3>
void _show(int i, const T1 &a, const T2 &b, const T3 &...c) {
    for (; a[i] != ',' && a[i] != '\0'; i++)
        cerr << a[i];
    cerr << ":" << b << " ";
    _show(i + 1, a, c...);
}
#line 2 "library/Utility/fastio.hpp"
#include <unistd.h>
namespace fastio {
static constexpr uint32_t SZ = 1 << 17;
char ibuf[SZ];
char obuf[SZ];
char out[100];
// pointer of ibuf, obuf

uint32_t pil = 0, pir = 0, por = 0;

struct Pre {
    char num[10000][4];
    constexpr Pre() : num() {
        for (int i = 0; i < 10000; i++) {
            int n = i;
            for (int j = 3; j >= 0; j--) {
                num[i][j] = n % 10 | '0';
                n /= 10;
            }
        }
    }
} constexpr pre;

inline void load() {
    memmove(ibuf, ibuf + pil, pir - pil);
    pir = pir - pil + fread(ibuf + pir - pil, 1, SZ - pir + pil, stdin);
    pil = 0;
    if (pir < SZ)
        ibuf[pir++] = '\n';
}

inline void flush() {
    fwrite(obuf, 1, por, stdout);
    por = 0;
}

void rd(char &c) {
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
}

void rd(string &x) {
    x.clear();
    char c;
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
    do {
        x += c;
        if (pil == pir)
            load();
        c = ibuf[pil++];
    } while (!isspace(c));
}

template <typename T> void rd_real(T &x) {
    string s;
    rd(s);
    x = stod(s);
}

template <typename T> void rd_integer(T &x) {
    if (pil + 100 > pir)
        load();
    char c;
    do
        c = ibuf[pil++];
    while (c < '-');
    bool minus = 0;
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (c == '-') {
            minus = 1, c = ibuf[pil++];
        }
    }
    x = 0;
    while ('0' <= c) {
        x = x * 10 + (c & 15), c = ibuf[pil++];
    }
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (minus)
            x = -x;
    }
}

void rd(int &x) {
    rd_integer(x);
}
void rd(ll &x) {
    rd_integer(x);
}
void rd(i128 &x) {
    rd_integer(x);
}
void rd(uint &x) {
    rd_integer(x);
}
void rd(ull &x) {
    rd_integer(x);
}
void rd(u128 &x) {
    rd_integer(x);
}
void rd(double &x) {
    rd_real(x);
}
void rd(long double &x) {
    rd_real(x);
}

template <class T, class U> void rd(pair<T, U> &p) {
    return rd(p.first), rd(p.second);
}
template <size_t N = 0, typename T> void rd_tuple(T &t) {
    if constexpr (N < std::tuple_size<T>::value) {
        auto &x = std::get<N>(t);
        rd(x);
        rd_tuple<N + 1>(t);
    }
}
template <class... T> void rd(tuple<T...> &tpl) {
    rd_tuple(tpl);
}

template <size_t N = 0, typename T> void rd(array<T, N> &x) {
    for (auto &d : x)
        rd(d);
}
template <class T> void rd(vector<T> &x) {
    for (auto &d : x)
        rd(d);
}

void read() {}
template <class H, class... T> void read(H &h, T &...t) {
    rd(h), read(t...);
}

void wt(const char c) {
    if (por == SZ)
        flush();
    obuf[por++] = c;
}
void wt(const string s) {
    for (char c : s)
        wt(c);
}
void wt(const char *s) {
    size_t len = strlen(s);
    for (size_t i = 0; i < len; i++)
        wt(s[i]);
}

template <typename T> void wt_integer(T x) {
    if (por > SZ - 100)
        flush();
    if (x < 0) {
        obuf[por++] = '-', x = -x;
    }
    int outi;
    for (outi = 96; x >= 10000; outi -= 4) {
        memcpy(out + outi, pre.num[x % 10000], 4);
        x /= 10000;
    }
    if (x >= 1000) {
        memcpy(obuf + por, pre.num[x], 4);
        por += 4;
    } else if (x >= 100) {
        memcpy(obuf + por, pre.num[x] + 1, 3);
        por += 3;
    } else if (x >= 10) {
        int q = (x * 103) >> 10;
        obuf[por] = q | '0';
        obuf[por + 1] = (x - q * 10) | '0';
        por += 2;
    } else
        obuf[por++] = x | '0';
    memcpy(obuf + por, out + outi + 4, 96 - outi);
    por += 96 - outi;
}

template <typename T> void wt_real(T x) {
    ostringstream oss;
    oss << fixed << setprecision(15) << double(x);
    string s = oss.str();
    wt(s);
}

void wt(int x) {
    wt_integer(x);
}
void wt(ll x) {
    wt_integer(x);
}
void wt(i128 x) {
    wt_integer(x);
}
void wt(uint x) {
    wt_integer(x);
}
void wt(ull x) {
    wt_integer(x);
}
void wt(u128 x) {
    wt_integer(x);
}
void wt(double x) {
    wt_real(x);
}
void wt(long double x) {
    wt_real(x);
}

template <class T, class U> void wt(const pair<T, U> val) {
    wt(val.first);
    wt(' ');
    wt(val.second);
}
template <size_t N = 0, typename T> void wt_tuple(const T t) {
    if constexpr (N < std::tuple_size<T>::value) {
        if constexpr (N > 0) {
            wt(' ');
        }
        const auto x = std::get<N>(t);
        wt(x);
        wt_tuple<N + 1>(t);
    }
}
template <class... T> void wt(tuple<T...> tpl) {
    wt_tuple(tpl);
}
template <class T, size_t S> void wt(const array<T, S> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}
template <class T> void wt(const vector<T> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}

void print() {
    wt('\n');
}
template <class Head, class... Tail> void print(Head &&head, Tail &&...tail) {
    wt(head);
    if (sizeof...(Tail))
        wt(' ');
    print(forward<Tail>(tail)...);
}
void __attribute__((destructor)) _d() {
    flush();
}
} // namespace fastio

using fastio::flush;
using fastio::print;
using fastio::read;

inline void first(bool i = true) {
    print(i ? "first" : "second");
}
inline void Alice(bool i = true) {
    print(i ? "Alice" : "Bob");
}
inline void Takahashi(bool i = true) {
    print(i ? "Takahashi" : "Aoki");
}
inline void yes(bool i = true) {
    print(i ? "yes" : "no");
}
inline void Yes(bool i = true) {
    print(i ? "Yes" : "No");
}
inline void No() {
    print("No");
}
inline void YES(bool i = true) {
    print(i ? "YES" : "NO");
}
inline void NO() {
    print("NO");
}
inline void Yay(bool i = true) {
    print(i ? "Yay!" : ":(");
}
inline void Possible(bool i = true) {
    print(i ? "Possible" : "Impossible");
}
inline void POSSIBLE(bool i = true) {
    print(i ? "POSSIBLE" : "IMPOSSIBLE");
}

/**
 * @brief Fast IO
 */
#line 3 "sol.cpp"

#line 2 "library/String/zalgo.hpp"

template<typename T>vector<int> Zalgo(T& s){
   int n=s.size(); vector<int> res(n);
   for(int i=1,j=0;i<n;i++){
      if(i+res[i-j]<j+res[j])res[i]=res[i-j];
      else{
         int k=max(0,j+res[j]-i);
         while(i+k<n&&s[k]==s[i+k])k++;
         res[i]=k; j=i;
      }
   } res[0]=n; return res;
}

/**
 * @brief Z-Algorithm
 */
#line 5 "sol.cpp"

vector<int> hungarian(const vector<vector<ll>> &a) {
    int n = a.size(), p, q;
    vector<ll> fx(n, inf), fy(n, 0);
    vector<int> x(n, -1), y(n, -1);
    rep(i, 0, n) rep(j, 0, n) chmax(fx[i], a[i][j]);
    for (int i = 0; i < n;) {
        vector<ll> t(n, -1), s(n + 1, i);
        for (p = q = 0; p <= q && x[i] < 0; p++) {
            for (int k = s[p], j = 0; j < n && x[i] < 0; j++) {
                if (fx[k] + fy[j] == a[k][j] && t[j] < 0) {
                    s[++q] = y[j], t[j] = k;
                    if (s[q] < 0)
                        for (p = j; p >= 0; j = p) {
                            y[j] = k = t[j], p = x[k], x[k] = j;
                        }
                }
            }
        }
        if (x[i] < 0) {
            ll d = INF;
            rep(k, 0, q + 1) rep(j, 0, n) if (t[j] < 0)
                chmin(d, fx[s[k]] + fy[j] - a[s[k]][j]);
            rep(j, 0, n) fy[j] += (t[j] < 0 ? 0 : d);
            rep(k, 0, q + 1) fx[s[k]] -= d;
        } else
            i++;
    }
    return x;
}

int main() {
    int n;
    read(n);
    vector<string> a(n);
    read(a);

    vector cost(n, vector<ll>(n));
    rep(i, 0, n) rep(j, 0, n) {
        auto S = a[j] + a[i];
        auto Z = Zalgo(S);
        cost[i][j] = -SZ(a[i]);
        rep(k, (i == j), SZ(a[i])) if (SZ(a[j]) + k + Z[SZ(a[j]) + k] >=
                                       SZ(a[i]) + SZ(a[j])) {
            cost[i][j] = -k;
            break;
        }
    }

    auto mate = hungarian(cost);
    ll ret = 0;
    rep(i, 0, n) ret -= cost[i][mate[i]];
    print(ret);
    return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 3804
final_rank: 4

code_snippet: #include <bits/stdc++.h>
using namespace std;

#define ll long long
#define elif else if
#define vi vector<int>
#define vll vector<ll>
#define vvi vector<vi>
#define rep(i, n) for (int i = 0; i < (n); i++)
#define rep2(i, l, r) for (int i = l; i < (r); i++)

template <typename T>
pair<T, vector<int>> hungarian(vector<vector<T>> &A)
{
  const T infty = numeric_limits<T>::max();
  const int N = (int)A.size();
  const int M = (int)A[0].size();
  vector<int> P(M), way(M);
  vector<T> U(N, 0), V(M, 0), minV;
  vector<bool> used;

  for (int i = 1; i < N; i++)
  {
    P[0] = i;
    minV.assign(M, infty);
    used.assign(M, false);
    int j0 = 0;
    while (P[j0] != 0)
    {
      int i0 = P[j0], j1 = 0;
      used[j0] = true;
      T delta = infty;
      for (int j = 1; j < M; j++)
      {
        if (used[j])
          continue;
        T curr = A[i0][j] - U[i0] - V[j];
        if (curr < minV[j])
          minV[j] = curr, way[j] = j0;
        if (minV[j] < delta)
          delta = minV[j], j1 = j;
      }
      for (int j = 0; j < M; j++)
      {
        if (used[j])
          U[P[j]] += delta, V[j] -= delta;
        else
          minV[j] -= delta;
      }
      j0 = j1;
    }
    do
    {
      P[j0] = P[way[j0]];
      j0 = way[j0];
    } while (j0 != 0);
  }
  return {-V[0], P};
}

vector<int> z_algorithm(const string &s)
{
  vector<int> prefix(s.size());
  for (int i = 1, j = 0; i < s.size(); i++)
  {
    if (i + prefix[i - j] < j + prefix[j])
    {
      prefix[i] = prefix[i - j];
    }
    else
    {
      int k = max(0, j + prefix[j] - i);
      while (i + k < s.size() && s[k] == s[i + k])
        ++k;
      prefix[i] = k;
      j = i;
    }
  }
  prefix[0] = (int)s.size();
  return prefix;
}

int calc(string &S, string &T)
{
  vi z = z_algorithm(T + S);
  int nS = S.size();
  int nT = T.size();
  rep2(i, 1, nS)
  {
    if (z[nT + i] == nS - i)
      return nS - i;
  }
  return 0;
}

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

  int N;
  cin >> N;
  vector<string> S(N);
  rep(i, N) cin >> S[i];
  vvi C(N + 1, vi(N + 1, 0));
  rep(i, N) rep(j, N) C[i + 1][j + 1] = -calc(S[i], S[j]);
  int ans = 0;
  rep(i, N) ans += S[i].size();
  cout << ans + hungarian(C).first << endl;
}
accuracy: 1
time_ms: 40
memory_kb: 3588
final_rank: 2

code_snippet: #include<bits/stdc++.h>
using namespace std;
using ll=long long;

std::vector<int> z_algorithm(const string& s) {
  int n = int(s.size());
  if (n == 0) return {};
  std::vector<int> z(n);
  z[0] = 0;
  for (int i = 1, j = 0; i < n; i++) {
    int& k = z[i];
    k = (j + z[j] <= i) ? 0 : std::min(j + z[j] - i, z[i - j]);
    while (i + k < n && s[k] == s[i + k]) k++;
    if (j + z[j] < i + z[i]) j = i;
  }
  z[0] = n;
  return z;
}

int main(){
    ll N;
    cin>>N;
    vector<string> S(N);
    ll an=0;
    for(int i=0;i<N;i++){
        cin>>S[i];
        an+=S[i].size();
    }

    vector<vector<ll>> D(N,vector<ll>(N,0));
    
    for(int i=0;i<N;i++){
        ll mx=0;
        string T=S[i];
        for(int j=0;j<N;j++){
            int s=S[j].size();
            T+="#"+S[j].substr(i==j,s-(i==j));
        }
        int n=T.size(),c=-1,k=0;
        auto z=z_algorithm(T);
        
        for(int j=0;j<n;j++){
            k++;
            if(T[j]=='#'){
                c++;
                k=0;
                if(c==i)k++;
            }
            if(c<0||k==0)continue;
            ll s=S[c].size();
            if(z[j]>=s-k+1)D[i][c]=max(D[i][c],s-k+1);
        }
    }

    vector<bool> use(N,0);
    vector<bool> used(N,0);
    vector<pair<ll,pair<ll,ll>>> SS;
    for(int i=0;i<N;i++)for(int j=0;j<N;j++)SS.push_back({D[j][i],{i,j}});

    sort(SS.begin(),SS.end());
    reverse(SS.begin(),SS.end());
    for(int i=0;i<N*N;i++){
        auto [c,v]=SS[i];
        auto [y,x]=v;
        if(use[y]||used[x])continue;
        an-=c;
        use[y]=used[x]=1;
    }
    cout<<an<<endl;
}
accuracy: 1
time_ms: 40
memory_kb: 4368
final_rank: 9

code_snippet: #include<bits/stdc++.h>
using namespace std;
using ll=long long;

std::vector<int> z_algorithm(const string& s) {
  int n = int(s.size());
  if (n == 0) return {};
  std::vector<int> z(n);
  z[0] = 0;
  for (int i = 1, j = 0; i < n; i++) {
    int& k = z[i];
    k = (j + z[j] <= i) ? 0 : std::min(j + z[j] - i, z[i - j]);
    while (i + k < n && s[k] == s[i + k]) k++;
    if (j + z[j] < i + z[i]) j = i;
  }
  z[0] = n;
  return z;
}




int main(){

    cin.tie(nullptr);
    ios::sync_with_stdio(false);

    ll N;
    cin>>N;
    vector<string> S(N);
    ll an=0;
    for(int i=0;i<N;i++){
        cin>>S[i];
        an+=S[i].size();
    }

    vector<vector<ll>> D(N,vector<ll>(N,0));
    
    
    for(int i=0;i<N;i++){
        ll mx=0;
        string T=S[i];
        for(int j=0;j<N;j++){
            int s=S[j].size();
            T+="#"+S[j].substr(i==j,s-(i==j));
        }
        int n=T.size();
        auto z=z_algorithm(T);
        int c=-1,k=0;
        
        for(int j=0;j<n;j++){
            k++;
            if(T[j]=='#'){
                c++;
                k=0;
                if(c==i)k++;
            }
            if(c<0||k==0)continue;
            ll s=S[c].size();
            if(z[j]>=s-k+1)D[i][c]=max(D[i][c],s-k+1);
        }
    }

    vector<bool> use(N,0);
    vector<bool> used(N,0);
    vector<pair<ll,pair<ll,ll>>> SS;
    for(int i=0;i<N;i++)for(int j=0;j<N;j++)SS.push_back({D[j][i],{i,j}});

    sort(SS.begin(),SS.end());
    reverse(SS.begin(),SS.end());
    for(int i=0;i<N*N;i++){
        auto [c,v]=SS[i];
        auto [y,x]=v;
        if(use[y]||used[x])continue;
        an-=c;
        use[y]=used[x]=1;
    }
    cout<<an<<endl;
}
accuracy: 1
time_ms: 50
memory_kb: 4228
final_rank: 7

code_snippet: #include<bits/stdc++.h>
using namespace std;
using ll=long long;


#include <algorithm>
#include <cassert>
#include <numeric>
#include <string>
#include <vector>

namespace atcoder {

namespace internal {

std::vector<int> sa_naive(const std::vector<int>& s) {
    int n = int(s.size());
    std::vector<int> sa(n);
    std::iota(sa.begin(), sa.end(), 0);
    std::sort(sa.begin(), sa.end(), [&](int l, int r) {
        if (l == r) return false;
        while (l < n && r < n) {
            if (s[l] != s[r]) return s[l] < s[r];
            l++;
            r++;
        }
        return l == n;
    });
    return sa;
}

std::vector<int> sa_doubling(const std::vector<int>& s) {
    int n = int(s.size());
    std::vector<int> sa(n), rnk = s, tmp(n);
    std::iota(sa.begin(), sa.end(), 0);
    for (int k = 1; k < n; k *= 2) {
        auto cmp = [&](int x, int y) {
            if (rnk[x] != rnk[y]) return rnk[x] < rnk[y];
            int rx = x + k < n ? rnk[x + k] : -1;
            int ry = y + k < n ? rnk[y + k] : -1;
            return rx < ry;
        };
        std::sort(sa.begin(), sa.end(), cmp);
        tmp[sa[0]] = 0;
        for (int i = 1; i < n; i++) {
            tmp[sa[i]] = tmp[sa[i - 1]] + (cmp(sa[i - 1], sa[i]) ? 1 : 0);
        }
        std::swap(tmp, rnk);
    }
    return sa;
}

// SA-IS, linear-time suffix array construction
// Reference:
// G. Nong, S. Zhang, and W. H. Chan,
// Two Efficient Algorithms for Linear Time Suffix Array Construction
template <int THRESHOLD_NAIVE = 10, int THRESHOLD_DOUBLING = 40>
std::vector<int> sa_is(const std::vector<int>& s, int upper) {
    int n = int(s.size());
    if (n == 0) return {};
    if (n == 1) return {0};
    if (n == 2) {
        if (s[0] < s[1]) {
            return {0, 1};
        } else {
            return {1, 0};
        }
    }
    if (n < THRESHOLD_NAIVE) {
        return sa_naive(s);
    }
    if (n < THRESHOLD_DOUBLING) {
        return sa_doubling(s);
    }

    std::vector<int> sa(n);
    std::vector<bool> ls(n);
    for (int i = n - 2; i >= 0; i--) {
        ls[i] = (s[i] == s[i + 1]) ? ls[i + 1] : (s[i] < s[i + 1]);
    }
    std::vector<int> sum_l(upper + 1), sum_s(upper + 1);
    for (int i = 0; i < n; i++) {
        if (!ls[i]) {
            sum_s[s[i]]++;
        } else {
            sum_l[s[i] + 1]++;
        }
    }
    for (int i = 0; i <= upper; i++) {
        sum_s[i] += sum_l[i];
        if (i < upper) sum_l[i + 1] += sum_s[i];
    }

    auto induce = [&](const std::vector<int>& lms) {
        std::fill(sa.begin(), sa.end(), -1);
        std::vector<int> buf(upper + 1);
        std::copy(sum_s.begin(), sum_s.end(), buf.begin());
        for (auto d : lms) {
            if (d == n) continue;
            sa[buf[s[d]]++] = d;
        }
        std::copy(sum_l.begin(), sum_l.end(), buf.begin());
        sa[buf[s[n - 1]]++] = n - 1;
        for (int i = 0; i < n; i++) {
            int v = sa[i];
            if (v >= 1 && !ls[v - 1]) {
                sa[buf[s[v - 1]]++] = v - 1;
            }
        }
        std::copy(sum_l.begin(), sum_l.end(), buf.begin());
        for (int i = n - 1; i >= 0; i--) {
            int v = sa[i];
            if (v >= 1 && ls[v - 1]) {
                sa[--buf[s[v - 1] + 1]] = v - 1;
            }
        }
    };

    std::vector<int> lms_map(n + 1, -1);
    int m = 0;
    for (int i = 1; i < n; i++) {
        if (!ls[i - 1] && ls[i]) {
            lms_map[i] = m++;
        }
    }
    std::vector<int> lms;
    lms.reserve(m);
    for (int i = 1; i < n; i++) {
        if (!ls[i - 1] && ls[i]) {
            lms.push_back(i);
        }
    }

    induce(lms);

    if (m) {
        std::vector<int> sorted_lms;
        sorted_lms.reserve(m);
        for (int v : sa) {
            if (lms_map[v] != -1) sorted_lms.push_back(v);
        }
        std::vector<int> rec_s(m);
        int rec_upper = 0;
        rec_s[lms_map[sorted_lms[0]]] = 0;
        for (int i = 1; i < m; i++) {
            int l = sorted_lms[i - 1], r = sorted_lms[i];
            int end_l = (lms_map[l] + 1 < m) ? lms[lms_map[l] + 1] : n;
            int end_r = (lms_map[r] + 1 < m) ? lms[lms_map[r] + 1] : n;
            bool same = true;
            if (end_l - l != end_r - r) {
                same = false;
            } else {
                while (l < end_l) {
                    if (s[l] != s[r]) {
                        break;
                    }
                    l++;
                    r++;
                }
                if (l == n || s[l] != s[r]) same = false;
            }
            if (!same) rec_upper++;
            rec_s[lms_map[sorted_lms[i]]] = rec_upper;
        }

        auto rec_sa =
            sa_is<THRESHOLD_NAIVE, THRESHOLD_DOUBLING>(rec_s, rec_upper);

        for (int i = 0; i < m; i++) {
            sorted_lms[i] = lms[rec_sa[i]];
        }
        induce(sorted_lms);
    }
    return sa;
}

}  // namespace internal

std::vector<int> suffix_array(const std::vector<int>& s, int upper) {
    assert(0 <= upper);
    for (int d : s) {
        assert(0 <= d && d <= upper);
    }
    auto sa = internal::sa_is(s, upper);
    return sa;
}

template <class T> std::vector<int> suffix_array(const std::vector<T>& s) {
    int n = int(s.size());
    std::vector<int> idx(n);
    iota(idx.begin(), idx.end(), 0);
    sort(idx.begin(), idx.end(), [&](int l, int r) { return s[l] < s[r]; });
    std::vector<int> s2(n);
    int now = 0;
    for (int i = 0; i < n; i++) {
        if (i && s[idx[i - 1]] != s[idx[i]]) now++;
        s2[idx[i]] = now;
    }
    return internal::sa_is(s2, now);
}

std::vector<int> suffix_array(const std::string& s) {
    int n = int(s.size());
    std::vector<int> s2(n);
    for (int i = 0; i < n; i++) {
        s2[i] = s[i];
    }
    return internal::sa_is(s2, 255);
}

// Reference:
// T. Kasai, G. Lee, H. Arimura, S. Arikawa, and K. Park,
// Linear-Time Longest-Common-Prefix Computation in Suffix Arrays and Its
// Applications
template <class T>
std::vector<int> lcp_array(const std::vector<T>& s,
                           const std::vector<int>& sa) {
    int n = int(s.size());
    assert(n >= 1);
    std::vector<int> rnk(n);
    for (int i = 0; i < n; i++) {
        rnk[sa[i]] = i;
    }
    std::vector<int> lcp(n - 1);
    int h = 0;
    for (int i = 0; i < n; i++) {
        if (h > 0) h--;
        if (rnk[i] == 0) continue;
        int j = sa[rnk[i] - 1];
        for (; j + h < n && i + h < n; h++) {
            if (s[j + h] != s[i + h]) break;
        }
        lcp[rnk[i] - 1] = h;
    }
    return lcp;
}

std::vector<int> lcp_array(const std::string& s, const std::vector<int>& sa) {
    int n = int(s.size());
    std::vector<int> s2(n);
    for (int i = 0; i < n; i++) {
        s2[i] = s[i];
    }
    return lcp_array(s2, sa);
}

// Reference:
// D. Gusfield,
// Algorithms on Strings, Trees, and Sequences: Computer Science and
// Computational Biology
template <class T> std::vector<int> z_algorithm(const std::vector<T>& s) {
    int n = int(s.size());
    if (n == 0) return {};
    std::vector<int> z(n);
    z[0] = 0;
    for (int i = 1, j = 0; i < n; i++) {
        int& k = z[i];
        k = (j + z[j] <= i) ? 0 : std::min(j + z[j] - i, z[i - j]);
        while (i + k < n && s[k] == s[i + k]) k++;
        if (j + z[j] < i + z[i]) j = i;
    }
    z[0] = n;
    return z;
}

std::vector<int> z_algorithm(const std::string& s) {
    int n = int(s.size());
    std::vector<int> s2(n);
    for (int i = 0; i < n; i++) {
        s2[i] = s[i];
    }
    return z_algorithm(s2);
}

}  // namespace atcoder

using namespace atcoder;

int main(){

    cin.tie(nullptr);
    ios::sync_with_stdio(false);

    ll N;
    cin>>N;
    vector<string> S(N);
    ll an=0;
    for(int i=0;i<N;i++){
        cin>>S[i];
        an+=S[i].size();
    }

    vector<vector<ll>> D(N,vector<ll>(N,0));
    
    
    for(int i=0;i<N;i++){
        ll mx=0;
        string T=S[i];
        for(int j=0;j<N;j++){
            int s=S[j].size();
            T+="#"+S[j].substr(i==j,s-(i==j));
        }
        int n=T.size();
        auto z=z_algorithm(T);
        int c=-1,k=0;
        
        for(int j=0;j<n;j++){
            k++;
            if(T[j]=='#'){
                c++;
                k=0;
                if(c==i)k++;
            }
            if(c<0||k==0)continue;
            ll s=S[c].size();
            if(z[j]>=s-k+1){
                D[i][c]=max(D[i][c],s-k+1);
            }
        }
    }

    
    vector<bool> use(N,0);
    vector<bool> used(N,0);
    vector<pair<ll,pair<ll,ll>>> SS;
    for(int i=0;i<N;i++){
        for(int j=0;j<N;j++){
            SS.push_back({D[j][i],{i,j}});
        }
    }
    sort(SS.begin(),SS.end());
    reverse(SS.begin(),SS.end());
    for(int i=0;i<N*N;i++){
        auto [c,v]=SS[i];
        auto [y,x]=v;
        if(use[y])continue;
        if(used[x])continue;
        an-=c;
        use[y]=1;
        used[x]=1;
;   }

    cout<<an<<endl;

}
accuracy: 1
time_ms: 60
memory_kb: 4476
final_rank: 10

code_snippet: #include<bits/stdc++.h>
using namespace std;
using ll=long long;


#include <algorithm>
#include <cassert>
#include <numeric>
#include <string>
#include <vector>

namespace atcoder {

namespace internal {

std::vector<int> sa_naive(const std::vector<int>& s) {
    int n = int(s.size());
    std::vector<int> sa(n);
    std::iota(sa.begin(), sa.end(), 0);
    std::sort(sa.begin(), sa.end(), [&](int l, int r) {
        if (l == r) return false;
        while (l < n && r < n) {
            if (s[l] != s[r]) return s[l] < s[r];
            l++;
            r++;
        }
        return l == n;
    });
    return sa;
}

std::vector<int> sa_doubling(const std::vector<int>& s) {
    int n = int(s.size());
    std::vector<int> sa(n), rnk = s, tmp(n);
    std::iota(sa.begin(), sa.end(), 0);
    for (int k = 1; k < n; k *= 2) {
        auto cmp = [&](int x, int y) {
            if (rnk[x] != rnk[y]) return rnk[x] < rnk[y];
            int rx = x + k < n ? rnk[x + k] : -1;
            int ry = y + k < n ? rnk[y + k] : -1;
            return rx < ry;
        };
        std::sort(sa.begin(), sa.end(), cmp);
        tmp[sa[0]] = 0;
        for (int i = 1; i < n; i++) {
            tmp[sa[i]] = tmp[sa[i - 1]] + (cmp(sa[i - 1], sa[i]) ? 1 : 0);
        }
        std::swap(tmp, rnk);
    }
    return sa;
}

// SA-IS, linear-time suffix array construction
// Reference:
// G. Nong, S. Zhang, and W. H. Chan,
// Two Efficient Algorithms for Linear Time Suffix Array Construction
template <int THRESHOLD_NAIVE = 10, int THRESHOLD_DOUBLING = 40>
std::vector<int> sa_is(const std::vector<int>& s, int upper) {
    int n = int(s.size());
    if (n == 0) return {};
    if (n == 1) return {0};
    if (n == 2) {
        if (s[0] < s[1]) {
            return {0, 1};
        } else {
            return {1, 0};
        }
    }
    if (n < THRESHOLD_NAIVE) {
        return sa_naive(s);
    }
    if (n < THRESHOLD_DOUBLING) {
        return sa_doubling(s);
    }

    std::vector<int> sa(n);
    std::vector<bool> ls(n);
    for (int i = n - 2; i >= 0; i--) {
        ls[i] = (s[i] == s[i + 1]) ? ls[i + 1] : (s[i] < s[i + 1]);
    }
    std::vector<int> sum_l(upper + 1), sum_s(upper + 1);
    for (int i = 0; i < n; i++) {
        if (!ls[i]) {
            sum_s[s[i]]++;
        } else {
            sum_l[s[i] + 1]++;
        }
    }
    for (int i = 0; i <= upper; i++) {
        sum_s[i] += sum_l[i];
        if (i < upper) sum_l[i + 1] += sum_s[i];
    }

    auto induce = [&](const std::vector<int>& lms) {
        std::fill(sa.begin(), sa.end(), -1);
        std::vector<int> buf(upper + 1);
        std::copy(sum_s.begin(), sum_s.end(), buf.begin());
        for (auto d : lms) {
            if (d == n) continue;
            sa[buf[s[d]]++] = d;
        }
        std::copy(sum_l.begin(), sum_l.end(), buf.begin());
        sa[buf[s[n - 1]]++] = n - 1;
        for (int i = 0; i < n; i++) {
            int v = sa[i];
            if (v >= 1 && !ls[v - 1]) {
                sa[buf[s[v - 1]]++] = v - 1;
            }
        }
        std::copy(sum_l.begin(), sum_l.end(), buf.begin());
        for (int i = n - 1; i >= 0; i--) {
            int v = sa[i];
            if (v >= 1 && ls[v - 1]) {
                sa[--buf[s[v - 1] + 1]] = v - 1;
            }
        }
    };

    std::vector<int> lms_map(n + 1, -1);
    int m = 0;
    for (int i = 1; i < n; i++) {
        if (!ls[i - 1] && ls[i]) {
            lms_map[i] = m++;
        }
    }
    std::vector<int> lms;
    lms.reserve(m);
    for (int i = 1; i < n; i++) {
        if (!ls[i - 1] && ls[i]) {
            lms.push_back(i);
        }
    }

    induce(lms);

    if (m) {
        std::vector<int> sorted_lms;
        sorted_lms.reserve(m);
        for (int v : sa) {
            if (lms_map[v] != -1) sorted_lms.push_back(v);
        }
        std::vector<int> rec_s(m);
        int rec_upper = 0;
        rec_s[lms_map[sorted_lms[0]]] = 0;
        for (int i = 1; i < m; i++) {
            int l = sorted_lms[i - 1], r = sorted_lms[i];
            int end_l = (lms_map[l] + 1 < m) ? lms[lms_map[l] + 1] : n;
            int end_r = (lms_map[r] + 1 < m) ? lms[lms_map[r] + 1] : n;
            bool same = true;
            if (end_l - l != end_r - r) {
                same = false;
            } else {
                while (l < end_l) {
                    if (s[l] != s[r]) {
                        break;
                    }
                    l++;
                    r++;
                }
                if (l == n || s[l] != s[r]) same = false;
            }
            if (!same) rec_upper++;
            rec_s[lms_map[sorted_lms[i]]] = rec_upper;
        }

        auto rec_sa =
            sa_is<THRESHOLD_NAIVE, THRESHOLD_DOUBLING>(rec_s, rec_upper);

        for (int i = 0; i < m; i++) {
            sorted_lms[i] = lms[rec_sa[i]];
        }
        induce(sorted_lms);
    }
    return sa;
}

}  // namespace internal

std::vector<int> suffix_array(const std::vector<int>& s, int upper) {
    assert(0 <= upper);
    for (int d : s) {
        assert(0 <= d && d <= upper);
    }
    auto sa = internal::sa_is(s, upper);
    return sa;
}

template <class T> std::vector<int> suffix_array(const std::vector<T>& s) {
    int n = int(s.size());
    std::vector<int> idx(n);
    iota(idx.begin(), idx.end(), 0);
    sort(idx.begin(), idx.end(), [&](int l, int r) { return s[l] < s[r]; });
    std::vector<int> s2(n);
    int now = 0;
    for (int i = 0; i < n; i++) {
        if (i && s[idx[i - 1]] != s[idx[i]]) now++;
        s2[idx[i]] = now;
    }
    return internal::sa_is(s2, now);
}

std::vector<int> suffix_array(const std::string& s) {
    int n = int(s.size());
    std::vector<int> s2(n);
    for (int i = 0; i < n; i++) {
        s2[i] = s[i];
    }
    return internal::sa_is(s2, 255);
}

// Reference:
// T. Kasai, G. Lee, H. Arimura, S. Arikawa, and K. Park,
// Linear-Time Longest-Common-Prefix Computation in Suffix Arrays and Its
// Applications
template <class T>
std::vector<int> lcp_array(const std::vector<T>& s,
                           const std::vector<int>& sa) {
    int n = int(s.size());
    assert(n >= 1);
    std::vector<int> rnk(n);
    for (int i = 0; i < n; i++) {
        rnk[sa[i]] = i;
    }
    std::vector<int> lcp(n - 1);
    int h = 0;
    for (int i = 0; i < n; i++) {
        if (h > 0) h--;
        if (rnk[i] == 0) continue;
        int j = sa[rnk[i] - 1];
        for (; j + h < n && i + h < n; h++) {
            if (s[j + h] != s[i + h]) break;
        }
        lcp[rnk[i] - 1] = h;
    }
    return lcp;
}

std::vector<int> lcp_array(const std::string& s, const std::vector<int>& sa) {
    int n = int(s.size());
    std::vector<int> s2(n);
    for (int i = 0; i < n; i++) {
        s2[i] = s[i];
    }
    return lcp_array(s2, sa);
}

// Reference:
// D. Gusfield,
// Algorithms on Strings, Trees, and Sequences: Computer Science and
// Computational Biology
template <class T> std::vector<int> z_algorithm(const std::vector<T>& s) {
    int n = int(s.size());
    if (n == 0) return {};
    std::vector<int> z(n);
    z[0] = 0;
    for (int i = 1, j = 0; i < n; i++) {
        int& k = z[i];
        k = (j + z[j] <= i) ? 0 : std::min(j + z[j] - i, z[i - j]);
        while (i + k < n && s[k] == s[i + k]) k++;
        if (j + z[j] < i + z[i]) j = i;
    }
    z[0] = n;
    return z;
}

std::vector<int> z_algorithm(const std::string& s) {
    int n = int(s.size());
    std::vector<int> s2(n);
    for (int i = 0; i < n; i++) {
        s2[i] = s[i];
    }
    return z_algorithm(s2);
}

}  // namespace atcoder

using namespace atcoder;

int main(){

    cin.tie(nullptr);
    ios::sync_with_stdio(false);

    ll N;
    cin>>N;
    vector<string> S(N);
    ll an=0;
    for(int i=0;i<N;i++){
        cin>>S[i];
        an+=S[i].size();
    }

    vector<vector<ll>> D(N,vector<ll>(N,0));
    for(int i=0;i<N;i++){
        ll mx=0;
        string T=S[i];
        for(int j=0;j<N;j++){
            int s=S[j].size();
            T+="#"+S[j].substr(i==j,s-(i==j));
        }
        int n=T.size();
        auto z=z_algorithm(T);
        int c=-1,k=0;
        
        for(int j=0;j<n;j++){
            k++;
            if(T[j]=='#'){
                c++;
                k=0;
                if(c==i)k++;
            }
            if(c<0||k==0)continue;
            ll s=S[c].size();
            if(z[j]>=s-k+1){
                D[i][c]=max(D[i][c],s-k+1);
            }
        }
    }
    vector<bool> use(N,0);
    for(int i=0;i<N;i++){
        ll mx=-1,id=-1;
        for(int j=0;j<N;j++){
            // if(use[j])continue;
            if(mx<D[j][i]){
                mx=D[j][i];
                id=j;
            }
        }
        an-=mx;
        use[id]=1;
    }

    cout<<an<<endl;

}
accuracy: 0.4523809523809524
time_ms: 60
memory_kb: 4432
final_rank: 17

code_snippet: #include<bits/stdc++.h>
using namespace std;
using ll=long long;


#include <algorithm>
#include <cassert>
#include <numeric>
#include <string>
#include <vector>

namespace atcoder {

namespace internal {

std::vector<int> sa_naive(const std::vector<int>& s) {
    int n = int(s.size());
    std::vector<int> sa(n);
    std::iota(sa.begin(), sa.end(), 0);
    std::sort(sa.begin(), sa.end(), [&](int l, int r) {
        if (l == r) return false;
        while (l < n && r < n) {
            if (s[l] != s[r]) return s[l] < s[r];
            l++;
            r++;
        }
        return l == n;
    });
    return sa;
}

std::vector<int> sa_doubling(const std::vector<int>& s) {
    int n = int(s.size());
    std::vector<int> sa(n), rnk = s, tmp(n);
    std::iota(sa.begin(), sa.end(), 0);
    for (int k = 1; k < n; k *= 2) {
        auto cmp = [&](int x, int y) {
            if (rnk[x] != rnk[y]) return rnk[x] < rnk[y];
            int rx = x + k < n ? rnk[x + k] : -1;
            int ry = y + k < n ? rnk[y + k] : -1;
            return rx < ry;
        };
        std::sort(sa.begin(), sa.end(), cmp);
        tmp[sa[0]] = 0;
        for (int i = 1; i < n; i++) {
            tmp[sa[i]] = tmp[sa[i - 1]] + (cmp(sa[i - 1], sa[i]) ? 1 : 0);
        }
        std::swap(tmp, rnk);
    }
    return sa;
}

// SA-IS, linear-time suffix array construction
// Reference:
// G. Nong, S. Zhang, and W. H. Chan,
// Two Efficient Algorithms for Linear Time Suffix Array Construction
template <int THRESHOLD_NAIVE = 10, int THRESHOLD_DOUBLING = 40>
std::vector<int> sa_is(const std::vector<int>& s, int upper) {
    int n = int(s.size());
    if (n == 0) return {};
    if (n == 1) return {0};
    if (n == 2) {
        if (s[0] < s[1]) {
            return {0, 1};
        } else {
            return {1, 0};
        }
    }
    if (n < THRESHOLD_NAIVE) {
        return sa_naive(s);
    }
    if (n < THRESHOLD_DOUBLING) {
        return sa_doubling(s);
    }

    std::vector<int> sa(n);
    std::vector<bool> ls(n);
    for (int i = n - 2; i >= 0; i--) {
        ls[i] = (s[i] == s[i + 1]) ? ls[i + 1] : (s[i] < s[i + 1]);
    }
    std::vector<int> sum_l(upper + 1), sum_s(upper + 1);
    for (int i = 0; i < n; i++) {
        if (!ls[i]) {
            sum_s[s[i]]++;
        } else {
            sum_l[s[i] + 1]++;
        }
    }
    for (int i = 0; i <= upper; i++) {
        sum_s[i] += sum_l[i];
        if (i < upper) sum_l[i + 1] += sum_s[i];
    }

    auto induce = [&](const std::vector<int>& lms) {
        std::fill(sa.begin(), sa.end(), -1);
        std::vector<int> buf(upper + 1);
        std::copy(sum_s.begin(), sum_s.end(), buf.begin());
        for (auto d : lms) {
            if (d == n) continue;
            sa[buf[s[d]]++] = d;
        }
        std::copy(sum_l.begin(), sum_l.end(), buf.begin());
        sa[buf[s[n - 1]]++] = n - 1;
        for (int i = 0; i < n; i++) {
            int v = sa[i];
            if (v >= 1 && !ls[v - 1]) {
                sa[buf[s[v - 1]]++] = v - 1;
            }
        }
        std::copy(sum_l.begin(), sum_l.end(), buf.begin());
        for (int i = n - 1; i >= 0; i--) {
            int v = sa[i];
            if (v >= 1 && ls[v - 1]) {
                sa[--buf[s[v - 1] + 1]] = v - 1;
            }
        }
    };

    std::vector<int> lms_map(n + 1, -1);
    int m = 0;
    for (int i = 1; i < n; i++) {
        if (!ls[i - 1] && ls[i]) {
            lms_map[i] = m++;
        }
    }
    std::vector<int> lms;
    lms.reserve(m);
    for (int i = 1; i < n; i++) {
        if (!ls[i - 1] && ls[i]) {
            lms.push_back(i);
        }
    }

    induce(lms);

    if (m) {
        std::vector<int> sorted_lms;
        sorted_lms.reserve(m);
        for (int v : sa) {
            if (lms_map[v] != -1) sorted_lms.push_back(v);
        }
        std::vector<int> rec_s(m);
        int rec_upper = 0;
        rec_s[lms_map[sorted_lms[0]]] = 0;
        for (int i = 1; i < m; i++) {
            int l = sorted_lms[i - 1], r = sorted_lms[i];
            int end_l = (lms_map[l] + 1 < m) ? lms[lms_map[l] + 1] : n;
            int end_r = (lms_map[r] + 1 < m) ? lms[lms_map[r] + 1] : n;
            bool same = true;
            if (end_l - l != end_r - r) {
                same = false;
            } else {
                while (l < end_l) {
                    if (s[l] != s[r]) {
                        break;
                    }
                    l++;
                    r++;
                }
                if (l == n || s[l] != s[r]) same = false;
            }
            if (!same) rec_upper++;
            rec_s[lms_map[sorted_lms[i]]] = rec_upper;
        }

        auto rec_sa =
            sa_is<THRESHOLD_NAIVE, THRESHOLD_DOUBLING>(rec_s, rec_upper);

        for (int i = 0; i < m; i++) {
            sorted_lms[i] = lms[rec_sa[i]];
        }
        induce(sorted_lms);
    }
    return sa;
}

}  // namespace internal

std::vector<int> suffix_array(const std::vector<int>& s, int upper) {
    assert(0 <= upper);
    for (int d : s) {
        assert(0 <= d && d <= upper);
    }
    auto sa = internal::sa_is(s, upper);
    return sa;
}

template <class T> std::vector<int> suffix_array(const std::vector<T>& s) {
    int n = int(s.size());
    std::vector<int> idx(n);
    iota(idx.begin(), idx.end(), 0);
    sort(idx.begin(), idx.end(), [&](int l, int r) { return s[l] < s[r]; });
    std::vector<int> s2(n);
    int now = 0;
    for (int i = 0; i < n; i++) {
        if (i && s[idx[i - 1]] != s[idx[i]]) now++;
        s2[idx[i]] = now;
    }
    return internal::sa_is(s2, now);
}

std::vector<int> suffix_array(const std::string& s) {
    int n = int(s.size());
    std::vector<int> s2(n);
    for (int i = 0; i < n; i++) {
        s2[i] = s[i];
    }
    return internal::sa_is(s2, 255);
}

// Reference:
// T. Kasai, G. Lee, H. Arimura, S. Arikawa, and K. Park,
// Linear-Time Longest-Common-Prefix Computation in Suffix Arrays and Its
// Applications
template <class T>
std::vector<int> lcp_array(const std::vector<T>& s,
                           const std::vector<int>& sa) {
    int n = int(s.size());
    assert(n >= 1);
    std::vector<int> rnk(n);
    for (int i = 0; i < n; i++) {
        rnk[sa[i]] = i;
    }
    std::vector<int> lcp(n - 1);
    int h = 0;
    for (int i = 0; i < n; i++) {
        if (h > 0) h--;
        if (rnk[i] == 0) continue;
        int j = sa[rnk[i] - 1];
        for (; j + h < n && i + h < n; h++) {
            if (s[j + h] != s[i + h]) break;
        }
        lcp[rnk[i] - 1] = h;
    }
    return lcp;
}

std::vector<int> lcp_array(const std::string& s, const std::vector<int>& sa) {
    int n = int(s.size());
    std::vector<int> s2(n);
    for (int i = 0; i < n; i++) {
        s2[i] = s[i];
    }
    return lcp_array(s2, sa);
}

// Reference:
// D. Gusfield,
// Algorithms on Strings, Trees, and Sequences: Computer Science and
// Computational Biology
template <class T> std::vector<int> z_algorithm(const std::vector<T>& s) {
    int n = int(s.size());
    if (n == 0) return {};
    std::vector<int> z(n);
    z[0] = 0;
    for (int i = 1, j = 0; i < n; i++) {
        int& k = z[i];
        k = (j + z[j] <= i) ? 0 : std::min(j + z[j] - i, z[i - j]);
        while (i + k < n && s[k] == s[i + k]) k++;
        if (j + z[j] < i + z[i]) j = i;
    }
    z[0] = n;
    return z;
}

std::vector<int> z_algorithm(const std::string& s) {
    int n = int(s.size());
    std::vector<int> s2(n);
    for (int i = 0; i < n; i++) {
        s2[i] = s[i];
    }
    return z_algorithm(s2);
}

}  // namespace atcoder

using namespace atcoder;

int main(){

    cin.tie(nullptr);
    ios::sync_with_stdio(false);

    ll N;
    cin>>N;
    vector<string> S(N);
    ll an=0;
    for(int i=0;i<N;i++){
        cin>>S[i];
        an+=S[i].size();
    }

    vector<vector<ll>> D(N,vector<ll>(N,0));
    for(int i=0;i<N;i++){
        ll mx=0;
        string T=S[i];
        for(int j=0;j<N;j++){
            int s=S[j].size();
            T+="#"+S[j].substr(i==j,s-(i==j));
        }
        int n=T.size();
        auto z=z_algorithm(T);
        int c=-1,k=0;
        
        for(int j=0;j<n;j++){
            k++;
            if(T[j]=='#'){
                c++;
                k=0;
                if(c==i)k++;
            }
            if(c<0||k==0)continue;
            ll s=S[c].size();
            if(z[j]>=s-k+1){
                D[i][c]=max(D[i][c],s-k+1);
            }
        }
    }
    vector<bool> use(N,0);
    for(int i=0;i<N;i++){
        ll mx=-1,id=-1;
        for(int j=0;j<N;j++){
            if(use[j])continue;
            if(mx<D[j][i]){
                mx=D[j][i];
                id=j;
            }
        }
        an-=mx;
        use[id]=1;
    }

    cout<<an<<endl;

}
accuracy: 0.14285714285714285
time_ms: 60
memory_kb: 4392
final_rank: 20

code_snippet: #include<bits/stdc++.h>
using namespace std;
using ll=long long;


#include <algorithm>
#include <cassert>
#include <numeric>
#include <string>
#include <vector>

namespace atcoder {

namespace internal {

std::vector<int> sa_naive(const std::vector<int>& s) {
    int n = int(s.size());
    std::vector<int> sa(n);
    std::iota(sa.begin(), sa.end(), 0);
    std::sort(sa.begin(), sa.end(), [&](int l, int r) {
        if (l == r) return false;
        while (l < n && r < n) {
            if (s[l] != s[r]) return s[l] < s[r];
            l++;
            r++;
        }
        return l == n;
    });
    return sa;
}

std::vector<int> sa_doubling(const std::vector<int>& s) {
    int n = int(s.size());
    std::vector<int> sa(n), rnk = s, tmp(n);
    std::iota(sa.begin(), sa.end(), 0);
    for (int k = 1; k < n; k *= 2) {
        auto cmp = [&](int x, int y) {
            if (rnk[x] != rnk[y]) return rnk[x] < rnk[y];
            int rx = x + k < n ? rnk[x + k] : -1;
            int ry = y + k < n ? rnk[y + k] : -1;
            return rx < ry;
        };
        std::sort(sa.begin(), sa.end(), cmp);
        tmp[sa[0]] = 0;
        for (int i = 1; i < n; i++) {
            tmp[sa[i]] = tmp[sa[i - 1]] + (cmp(sa[i - 1], sa[i]) ? 1 : 0);
        }
        std::swap(tmp, rnk);
    }
    return sa;
}

// SA-IS, linear-time suffix array construction
// Reference:
// G. Nong, S. Zhang, and W. H. Chan,
// Two Efficient Algorithms for Linear Time Suffix Array Construction
template <int THRESHOLD_NAIVE = 10, int THRESHOLD_DOUBLING = 40>
std::vector<int> sa_is(const std::vector<int>& s, int upper) {
    int n = int(s.size());
    if (n == 0) return {};
    if (n == 1) return {0};
    if (n == 2) {
        if (s[0] < s[1]) {
            return {0, 1};
        } else {
            return {1, 0};
        }
    }
    if (n < THRESHOLD_NAIVE) {
        return sa_naive(s);
    }
    if (n < THRESHOLD_DOUBLING) {
        return sa_doubling(s);
    }

    std::vector<int> sa(n);
    std::vector<bool> ls(n);
    for (int i = n - 2; i >= 0; i--) {
        ls[i] = (s[i] == s[i + 1]) ? ls[i + 1] : (s[i] < s[i + 1]);
    }
    std::vector<int> sum_l(upper + 1), sum_s(upper + 1);
    for (int i = 0; i < n; i++) {
        if (!ls[i]) {
            sum_s[s[i]]++;
        } else {
            sum_l[s[i] + 1]++;
        }
    }
    for (int i = 0; i <= upper; i++) {
        sum_s[i] += sum_l[i];
        if (i < upper) sum_l[i + 1] += sum_s[i];
    }

    auto induce = [&](const std::vector<int>& lms) {
        std::fill(sa.begin(), sa.end(), -1);
        std::vector<int> buf(upper + 1);
        std::copy(sum_s.begin(), sum_s.end(), buf.begin());
        for (auto d : lms) {
            if (d == n) continue;
            sa[buf[s[d]]++] = d;
        }
        std::copy(sum_l.begin(), sum_l.end(), buf.begin());
        sa[buf[s[n - 1]]++] = n - 1;
        for (int i = 0; i < n; i++) {
            int v = sa[i];
            if (v >= 1 && !ls[v - 1]) {
                sa[buf[s[v - 1]]++] = v - 1;
            }
        }
        std::copy(sum_l.begin(), sum_l.end(), buf.begin());
        for (int i = n - 1; i >= 0; i--) {
            int v = sa[i];
            if (v >= 1 && ls[v - 1]) {
                sa[--buf[s[v - 1] + 1]] = v - 1;
            }
        }
    };

    std::vector<int> lms_map(n + 1, -1);
    int m = 0;
    for (int i = 1; i < n; i++) {
        if (!ls[i - 1] && ls[i]) {
            lms_map[i] = m++;
        }
    }
    std::vector<int> lms;
    lms.reserve(m);
    for (int i = 1; i < n; i++) {
        if (!ls[i - 1] && ls[i]) {
            lms.push_back(i);
        }
    }

    induce(lms);

    if (m) {
        std::vector<int> sorted_lms;
        sorted_lms.reserve(m);
        for (int v : sa) {
            if (lms_map[v] != -1) sorted_lms.push_back(v);
        }
        std::vector<int> rec_s(m);
        int rec_upper = 0;
        rec_s[lms_map[sorted_lms[0]]] = 0;
        for (int i = 1; i < m; i++) {
            int l = sorted_lms[i - 1], r = sorted_lms[i];
            int end_l = (lms_map[l] + 1 < m) ? lms[lms_map[l] + 1] : n;
            int end_r = (lms_map[r] + 1 < m) ? lms[lms_map[r] + 1] : n;
            bool same = true;
            if (end_l - l != end_r - r) {
                same = false;
            } else {
                while (l < end_l) {
                    if (s[l] != s[r]) {
                        break;
                    }
                    l++;
                    r++;
                }
                if (l == n || s[l] != s[r]) same = false;
            }
            if (!same) rec_upper++;
            rec_s[lms_map[sorted_lms[i]]] = rec_upper;
        }

        auto rec_sa =
            sa_is<THRESHOLD_NAIVE, THRESHOLD_DOUBLING>(rec_s, rec_upper);

        for (int i = 0; i < m; i++) {
            sorted_lms[i] = lms[rec_sa[i]];
        }
        induce(sorted_lms);
    }
    return sa;
}

}  // namespace internal

std::vector<int> suffix_array(const std::vector<int>& s, int upper) {
    assert(0 <= upper);
    for (int d : s) {
        assert(0 <= d && d <= upper);
    }
    auto sa = internal::sa_is(s, upper);
    return sa;
}

template <class T> std::vector<int> suffix_array(const std::vector<T>& s) {
    int n = int(s.size());
    std::vector<int> idx(n);
    iota(idx.begin(), idx.end(), 0);
    sort(idx.begin(), idx.end(), [&](int l, int r) { return s[l] < s[r]; });
    std::vector<int> s2(n);
    int now = 0;
    for (int i = 0; i < n; i++) {
        if (i && s[idx[i - 1]] != s[idx[i]]) now++;
        s2[idx[i]] = now;
    }
    return internal::sa_is(s2, now);
}

std::vector<int> suffix_array(const std::string& s) {
    int n = int(s.size());
    std::vector<int> s2(n);
    for (int i = 0; i < n; i++) {
        s2[i] = s[i];
    }
    return internal::sa_is(s2, 255);
}

// Reference:
// T. Kasai, G. Lee, H. Arimura, S. Arikawa, and K. Park,
// Linear-Time Longest-Common-Prefix Computation in Suffix Arrays and Its
// Applications
template <class T>
std::vector<int> lcp_array(const std::vector<T>& s,
                           const std::vector<int>& sa) {
    int n = int(s.size());
    assert(n >= 1);
    std::vector<int> rnk(n);
    for (int i = 0; i < n; i++) {
        rnk[sa[i]] = i;
    }
    std::vector<int> lcp(n - 1);
    int h = 0;
    for (int i = 0; i < n; i++) {
        if (h > 0) h--;
        if (rnk[i] == 0) continue;
        int j = sa[rnk[i] - 1];
        for (; j + h < n && i + h < n; h++) {
            if (s[j + h] != s[i + h]) break;
        }
        lcp[rnk[i] - 1] = h;
    }
    return lcp;
}

std::vector<int> lcp_array(const std::string& s, const std::vector<int>& sa) {
    int n = int(s.size());
    std::vector<int> s2(n);
    for (int i = 0; i < n; i++) {
        s2[i] = s[i];
    }
    return lcp_array(s2, sa);
}

// Reference:
// D. Gusfield,
// Algorithms on Strings, Trees, and Sequences: Computer Science and
// Computational Biology
template <class T> std::vector<int> z_algorithm(const std::vector<T>& s) {
    int n = int(s.size());
    if (n == 0) return {};
    std::vector<int> z(n);
    z[0] = 0;
    for (int i = 1, j = 0; i < n; i++) {
        int& k = z[i];
        k = (j + z[j] <= i) ? 0 : std::min(j + z[j] - i, z[i - j]);
        while (i + k < n && s[k] == s[i + k]) k++;
        if (j + z[j] < i + z[i]) j = i;
    }
    z[0] = n;
    return z;
}

std::vector<int> z_algorithm(const std::string& s) {
    int n = int(s.size());
    std::vector<int> s2(n);
    for (int i = 0; i < n; i++) {
        s2[i] = s[i];
    }
    return z_algorithm(s2);
}

}  // namespace atcoder

using namespace atcoder;

int main(){

    cin.tie(nullptr);
    ios::sync_with_stdio(false);

    ll N;
    cin>>N;
    vector<string> S(N);
    ll an=0;
    for(int i=0;i<N;i++){
        cin>>S[i];
        an+=S[i].size();
    }

    vector<vector<ll>> D(N,vector<ll>(N,0));
    for(int i=0;i<N;i++){
        ll mx=0;
        string T=S[i];
        for(int j=0;j<N;j++){
            int s=S[j].size();
            T+="#"+S[j].substr(i==j,s-(i==j));
        }
        int n=T.size();
        auto z=z_algorithm(T);
        int c=-1,k=0;
        
        for(int j=0;j<n;j++){
            k++;
            if(T[j]=='#'){
                c++;
                k=0;
                if(c==i)k++;
            }
            if(c<0||k==0)continue;
            ll s=S[c].size();
            if(z[j]>=s-k+1){
                D[i][c]=max(D[i][c],s-k+1);
            }
        }
    }
    for(int i=0;i<N;i++){
        ll mx=0;
        for(int j=0;j<N;j++){
            mx=max(mx,D[j][i]);
        }
        an-=mx;
    }

    cout<<an<<endl;

}
accuracy: 0.4523809523809524
time_ms: 60
memory_kb: 4472
final_rank: 18

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, s, t) for (int i = (int)(s); i < (int)(t); ++i)
#define revrep(i, t, s) for (int i = (int)(t)-1; i >= (int)(s); --i)
#define all(x) begin(x), end(x)
template <typename T> bool chmax(T& a, const T& b) { return a < b ? (a = b, 1) : 0; }
template <typename T> bool chmin(T& a, const T& b) { return a > b ? (a = b, 1) : 0; }


template <typename Cap, typename Cost>
class MinCostFlow {
   public:
    MinCostFlow() = default;
    explicit MinCostFlow(int V) : V(V), G(V), add(0) {}

    void add_edge(int u, int v, Cap cap, Cost cost) {
        G[u].emplace_back(v, cap, cost, (int)G[v].size());
        G[v].emplace_back(u, 0, -cost, (int)G[u].size() - 1);
    }

    void add_edge(int u, int v, Cap lb, Cap ub, Cost cost) {
        add_edge(u, v, ub - lb, cost);
        add_edge(u, v, lb, cost - M);
        add += M * lb;
    }

    Cost min_cost_flow(int s, int t, Cap f, bool arbitrary = false) {
        Cost ret = add;
        std::vector<Cost> dist(V);
        std::vector<int> prevv(V), preve(V);
        using P = std::pair<Cost, int>;
        std::priority_queue<P, std::vector<P>, std::greater<P>> pq;

        auto h = calculate_initial_potential(s);

        while (f > 0) {
            // update h using dijkstra
            // std::ranges::fill(dist, INF);
            fill(all(dist), INF);
            dist[s] = 0;
            pq.emplace(0, s);
            while (!pq.empty()) {
                auto [d, v] = pq.top();
                pq.pop();
                if (dist[v] < d) continue;
                for (int i = 0; i < (int)G[v].size(); ++i) {
                    Edge& e = G[v][i];
                    Cost ndist = dist[v] + e.cost + h[v] - h[e.to];
                    if (e.cap > 0 && dist[e.to] > ndist) {
                        dist[e.to] = ndist;
                        prevv[e.to] = v;
                        preve[e.to] = i;
                        pq.emplace(dist[e.to], e.to);
                    }
                }
            }

            if (!arbitrary && dist[t] == INF) return -1;
            for (int v = 0; v < V; ++v) h[v] += dist[v];

            if (arbitrary && h[t] >= 0) break;

            Cap d = f;
            for (int v = t; v != s; v = prevv[v]) {
                d = std::min(d, G[prevv[v]][preve[v]].cap);
            }
            f -= d;
            ret += d * h[t];
            for (int v = t; v != s; v = prevv[v]) {
                Edge& e = G[prevv[v]][preve[v]];
                e.cap -= d;
                G[v][e.rev].cap += d;
            }
        }
        return ret;
    }

   private:
    struct Edge {
        int to;
        Cap cap;
        Cost cost;
        int rev;
        Edge(int to, Cap cap, Cost cost, int rev)
            : to(to), cap(cap), cost(cost), rev(rev) {}
    };

    static constexpr Cost INF = std::numeric_limits<Cost>::max() / 2;
    // large constant used for minimum flow requirement for edges
    static constexpr Cost M = INF / 1e9;

    int V;
    std::vector<std::vector<Edge>> G;
    Cost add;

    std::vector<Cost> calculate_initial_potential(int s) {
        std::vector<Cost> h(V);
        // if all costs are nonnegative, then do nothing
        return h;

        // if there is a negative edge,
        // use Bellman-Ford or topological sort and a DP (for DAG)
        // std::fill(h.begin(), h.end(), INF);
        // h[s] = 0;
        // for (int i = 0; i < V - 1; ++i) {
        //     for (int v = 0; v < V; ++v) {
        //         for (auto& e : G[v]) {
        //             if (e.cap > 0 && h[v] != INF && h[e.to] > h[v] + e.cost)
        //             {
        //                 h[e.to] = h[v] + e.cost;
        //             }
        //         }
        //     }
        // }

        // return h;
    }
};


class RollingHash {
   public:
    static long long generate_base() {
        std::random_device rd;
        std::mt19937_64 rng(rd());
        std::uniform_int_distribution<long long> rand(1, mod - 1);
        return rand(rng);
    }

    RollingHash() = default;
    RollingHash(const std::string& s, long long base)
        : RollingHash(std::vector<char>(s.begin(), s.end()), base) {}
    template <typename T>
    RollingHash(const std::vector<T>& s, long long base)
        : base(base), hashed(s.size() + 1), power(s.size() + 1) {
        power[0] = 1;
        for (int i = 0; i < (int)s.size(); ++i) {
            power[i + 1] = mul(power[i], base);
            hashed[i + 1] = add(mul(hashed[i], base), s[i]);
        }
    }

    long long query(int l, int r) const {
        return add(hashed[r], mod - mul(hashed[l], power[r - l]));
    }

    long long combine(long long h1, long long h2, int len2) const {
        return add(mul(h1, power[len2]), h2);
    }

    void push_back(char c) {
        power.push_back(mul(power.back(), base));
        hashed.push_back(add(mul(hashed.back(), base), c));
    }

   private:
    static constexpr long long mod = (1LL << 61) - 1;

    static inline long long add(long long a, long long b) {
        if ((a += b) >= mod) a -= mod;
        return a;
    }

    static inline long long mul(long long a, long long b) {
        __int128_t c = (__int128_t)a * b;
        return add(c >> 61, c & mod);
    }

    const long long base;
    std::vector<long long> hashed, power;
};


int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout << fixed << setprecision(15);

    int N;
    cin>>N;
    vector<string> S(N);
    for(auto& s:S)cin>>s;

    const ll base = RollingHash::generate_base();
    vector<RollingHash> rhs;
    for (auto&s:S) rhs.emplace_back(s, base);

    MinCostFlow<int,int> flow(2*N+2);
    int s = 2*N, t=2*N+1;
    rep(i,0,N) {
        flow.add_edge(s, i, 1, 0);
        flow.add_edge(N+i, t, 1, 0);
    }
    rep(i,0,N)rep(j,0,N){
        if (i==j){
            int unit = S[i].size();
            rep(k,1,S[i].size()){
                bool ok = true;
                for(int l = 0; l < S[i].size(); l += k){
                    int len = S[i].size() - l;
                    if (rhs[i].query(0,len) != rhs[i].query(l,l+len)){
                        ok = false;
                        break;
                    }
                }
                if (ok) {
                    unit = k;
                    break;
                }
            }
            // cout << "unit: " << i << " " << unit << endl;
            flow.add_edge(i, N+i, 1, unit);
        }else{
            int overlap = 0;
            rep(k,1,min(S[i].size(),S[j].size())+1){
                if (rhs[i].query(S[i].size()-k,S[i].size()) == rhs[j].query(0, k)) {
                    overlap = k;
                }
            }
            flow.add_edge(i, N+j, 1, S[j].size()-overlap);
        }
    }
    cout << flow.min_cost_flow(s, t, N) << endl;
}
accuracy: 1
time_ms: 30
memory_kb: 5420
final_rank: 11

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
template <class T>
using V = vector<T>;

#define foa(s, v) for (auto &s : v)
#define all(v) v.begin(), v.end()
#define sz(v) int(v.size())
#define rep(i, n) for (int i = 0; i < int(n); i++)

template <class T>
bool chmin(T &a, T b)
{
    return a > b ? a = b, 1 : 0;
}
template <class T>
bool chmax(T &a, T b) { return a < b ? a = b, 1 : 0; }

using bint = __int128_t;
constexpr bint mod = (1LL << 61) - 1;
struct hint
{
    bint x;
    hint(bint b = 0) : x(b % mod)
    {
        if (x < 0)
            x += mod;
    }
    hint operator+(hint b) { return hint(x + b.x); }
    hint operator-(hint b) { return hint(x - b.x); }
    hint operator*(hint b) { return hint(x * b.x); }
    hint &operator*=(hint b)
    {
        *this = *this * b;
        return *this;
    }
    hint operator^(bint n)
    {
        hint mult = x;
        hint ret = 1;
        while (n > 0)
        {
            if (n % 2)
                ret *= mult;
            mult *= mult;
            n /= 2;
        }
        return ret;
    }
};

const hint base = 1e9 + 9;

struct rolling_hash
{
    vector<hint> pw, data;
    rolling_hash(string s) : pw(sz(s) + 1, 1), data(sz(s) + 1, 0)
    {
        rep(i, sz(s))
        {
            pw[i + 1] = pw[i] * base;
            data[i + 1] = data[i] * base + s[i];
        }
    }

    hint get(int lef, int rig)
    {
        return data[rig] - data[lef] * pw.at(rig - lef);
    }
};

class mcf
{
public:
    using pti = pair<ll, int>;
    struct edge
    {
        int to;
        ll cap;
        ll cost;
        int rev;
    };
    const int ver;
    vector<vector<edge>> g;
    vector<ll> h, dist;
    vector<int> prevv, preve;
    mcf(int ns) : ver(ns), g(ver), h(ver, 0), dist(ver), prevv(ver), preve(ver) {}

    void add_edge(int from, int to, ll cap, ll cost)
    {
        // g[from].emplace_back(to, cap, cost, sz(g[to]));
        // g[to].emplace_back(from, 0, -cost, sz(g[from]) - 1);

        g[from].push_back(edge{to, cap, cost, sz(g[to])});
        g[to].push_back(edge{from, 0, -cost, sz(g[from]) - 1});
    }

    template <class T>
    using pqmin = priority_queue<T, vector<T>, greater<T>>;

    const ll INF = 1e18;

    ll solve(int s, int t, ll f)
    {
        ll res = 0;
        while (f > 0)
        {
            pqmin<pti> que;
            fill(dist.begin(), dist.end(), INF);
            dist[s] = 0;
            que.push(pti(0, s));
            while (que.size())
            {
                pti p = que.top();
                que.pop();
                int v = p.second;
                if (dist[v] < p.first)
                    continue;
                rep(i, sz(g[v]))
                {
                    edge &e = g[v][i];
                    ll nxtcost = dist[v] + e.cost + h[v] - h[e.to];
                    if (e.cap > 0 && chmin(dist[e.to], nxtcost))
                    {
                        prevv[e.to] = v, preve[e.to] = i;
                        que.push(pti(dist[e.to], e.to));
                    }
                }
            }

            if (dist[t] == INF)
                return -1;
            rep(i, ver) if (dist[i] != INF) h[i] += dist[i];
            ll d = f;
            for (int v = t; v != s; v = prevv[v])
                chmin(d, g[prevv[v]][preve[v]].cap);
            f -= d;
            res += h[t] * d;
            for (int v = t; v != s; v = prevv[v])
            {
                edge &e = g[prevv[v]][preve[v]];
                e.cap -= d;
                g[v][e.rev].cap += d;
            }
        }

        return res;
    }
};

int n;
void solve()
{
    vector<string> s(n);
    string t;
    vector<int> lasts = {0};
    foa(c, s)
    {
        cin >> c;
        t += c;
        lasts.push_back(sz(t));
    }

    rolling_hash rh(t);
    vector<vector<int>> costs(n, vector<int>(n));

    rep(from, n) rep(to, n)
    {
        string &fs = s[from];
        string &ts = s[to];

        for (int len = min(sz(fs), sz(ts)) - 1; len >= 0; len--)
        {
            int fl = lasts[from + 1] - len;
            int fr = lasts[from + 1];
            int tl = lasts[to];
            int tr = tl + len;
            if (rh.get(fl, fr).x == rh.get(tl, tr).x)
            {
                costs[from][to] = sz(ts) - len;
                break;
            }
        }
    }

    vector<int> leftnode(n), rightnode(n);
    int ver = 0;
    int source = ver++;
    int terminal = ver++;
    foa(c, leftnode) c = ver++;
    foa(c, rightnode) c = ver++;

    mcf g(ver);
    rep(lef, n)
    {
        g.add_edge(source, leftnode[lef], 1LL, 0LL);
    }
    rep(rig, n) g.add_edge(rightnode[rig], terminal, 1LL, 0LL);

    rep(lef, n) rep(rig, n)
    {
        g.add_edge(leftnode[lef], rightnode[rig], 1LL, costs[lef][rig]);
    }

    ll res = g.solve(source, terminal, n);
    cout << res << endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    while (cin >> n && n)
        solve();
}
accuracy: 1
time_ms: 640
memory_kb: 7496
final_rank: 16

code_snippet: #include <algorithm>
#include <bitset>
#include <cassert>
#include <chrono>
#include <climits>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <functional>
#include <iomanip>
#include <iostream>
#include <limits>
#include <map>
#include <memory>
#include <numeric>
#include <optional>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

/* macro */

#define rep(i, a, n) for (int i = (int)(a); i < (int)(n); i++)
#define rrep(i, a, n) for (int i = ((int)(n - 1)); i >= (int)(a); i--)
#define Rep(i, a, n) for (i64 i = (i64)(a); i < (i64)(n); i++)
#define RRep(i, a, n) for (i64 i = ((i64)(n - i64(1))); i >= (i64)(a); i--)
#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()
#define Bit(n) (1LL << (n))

/* macro end */

/* template */

namespace ebi {

#ifdef LOCAL
#define debug(...)                                                      \
    std::cerr << "LINE: " << __LINE__ << "  [" << #__VA_ARGS__ << "]:", \
        debug_out(__VA_ARGS__)
#else
#define debug(...)
#endif

void debug_out() { std::cerr << std::endl; }

template <typename Head, typename... Tail>
void debug_out(Head h, Tail... t) {
    std::cerr << " " << h;
    if (sizeof...(t) > 0) std::cout << " :";
    debug_out(t...);
}

template <typename T1, typename T2>
std::ostream &operator<<(std::ostream &os, const std::pair<T1, T2> &pa) {
    return os << pa.first << " " << pa.second;
}

template <typename T1, typename T2>
std::istream &operator>>(std::istream &os, std::pair<T1, T2> &pa) {
    return os >> pa.first >> pa.second;
}

template <typename T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &vec) {
    for (std::size_t i = 0; i < vec.size(); i++)
        os << vec[i] << (i + 1 == vec.size() ? "" : " ");
    return os;
}

template <typename T>
std::istream &operator>>(std::istream &os, std::vector<T> &vec) {
    for (T &e : vec) std::cin >> e;
    return os;
}

template <typename T>
std::ostream &operator<<(std::ostream &os, const std::optional<T> &opt) {
    if (opt) {
        os << opt.value();
    } else {
        os << "invalid value";
    }
    return os;
}

using std::size_t;
using i32 = std::int32_t;
using u32 = std::uint32_t;
using i64 = std::int64_t;
using u64 = std::uint64_t;

template <class T, T init>
auto make_vector(int n) {
    return std::vector<T>(n, init);
}

template <class T, T init, typename Head, typename... Tail>
auto make_vector(Head n, Tail... ts) {
    return std::vector(n, make_vector<T, init>(ts...));
}

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

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

template <class T>
T pow(T x, i64 n) {
    T res = 1;
    while (n > 0) {
        if (n & 1) res = res * x;
        x = x * x;
        n >>= 1;
    }
    return res;
}

template <class T>
T mod_pow(T x, i64 n, i64 mod) {
    T res = 1;
    while (n > 0) {
        if (n & 1) res = (res * x) % mod;
        x = (x * x) % mod;
        n >>= 1;
    }
    return res;
}

template <class T>
T scan() {
    T val;
    std::cin >> val;
    return val;
}

template <class T>
struct Edge {
    int to;
    T cost;
    Edge(int _to, T _cost = 1) : to(_to), cost(_cost) {}
};

template <class T>
struct Graph : std::vector<std::vector<Edge<T>>> {
    using std::vector<std::vector<Edge<T>>>::vector;
    void add_edge(int u, int v, T w, bool directed = false) {
        (*this)[u].emplace_back(v, w);
        if (directed) return;
        (*this)[v].emplace_back(u, w);
    }
};

struct graph : std::vector<std::vector<int>> {
    using std::vector<std::vector<int>>::vector;
    void add_edge(int u, int v, bool directed = false) {
        (*this)[u].emplace_back(v);
        if (directed) return;
        (*this)[v].emplace_back(u);
    }
};

constexpr i64 LNF = std::numeric_limits<i64>::max() / 4;

constexpr int INF = std::numeric_limits<int>::max() / 2;

// const std::vector<int> dy = {1, 0, -1, 0, 1, 1, -1, -1};
// const std::vector<int> dx = {0, 1, 0, -1, 1, -1, 1, -1};

const std::vector<int> dy = {-1, 0, 1, 0};
const std::vector<int> dx = {0, 1, 0, -1};

}  // namespace ebi

namespace ebi {

struct random_number_generator_64 {
private:
    using u64 = std::uint64_t;
    std::random_device rnd;
    std::mt19937_64 mt;
public:
    random_number_generator_64() : mt(rnd()) { }

    u64 get(u64 a, u64 b) {
        std::uniform_int_distribution<u64> dist(a, b-1);
        return dist(mt);
    }
};

}

/*
    reference: https://qiita.com/keymoon/items/11fac5627672a6d6a9f6
*/

namespace ebi {

template<int n>
struct rolling_hash {
private:
    using u64 = std::uint64_t;
    const u64 mod = (1UL<<61) - 1;
    const u64 MASK31 = (1UL<<31) - 1;
    const u64 MASK30 = (1UL<<30) - 1;
    const u64 h = 100;
    const u64 buffer = mod * 4;

    u64 safe_mul(const u64 &a, const u64 &b) const {
        u64 au = a>>31, ad = a&MASK31;
        u64 bu = b>>31, bd = b&MASK31;
        u64 mid = ad*bu + au*bd;
        u64 midu = mid>>30;
        u64 midd = mid&MASK30;
        return (au * bu * 2 + midu + (midd << 31) + ad * bd);
    }

    u64 safe_mod(const u64 &a) const {
        u64 au = a >> 61;
        u64 ad = a & mod;
        u64 res = au + ad;
        if(res >= mod) res -= mod;
        return res;
    }
public:
    rolling_hash(const std::string &s) : sz(s.size()) {
        assert(int(base.size()) == n && n > 0);
        base_pow.resize(n);
        hash.resize(n);
        for(int i = 0; i < n; ++i) {
            base_pow[i].reserve(sz+1);
            base_pow[i].emplace_back(1);
            hash[i].reserve(sz+1);
            hash[i].emplace_back(0);
            for(const auto &c: s) {
                hash[i].emplace_back(safe_mod(safe_mul(hash[i].back(), base[i]) + c + h));
                base_pow[i].emplace_back(safe_mod(safe_mul(base_pow[i].back(), base[i])));
            }
        }
    }

    // [l, r)

    std::vector<u64> get_hash(int l, int r) const {
        std::vector<u64> ret(n);
        for(int i = 0; i < n; ++i) {
            ret[i] = safe_mod(hash[i][r] + buffer - safe_mul(hash[i][l], base_pow[i][r-l]));
        }
        return ret;
    }

    std::vector<u64> get_hash(const std::string &str, int l = 0, int r = -1) const {
        if(r < 0) r = int(str.size());
        std::vector<u64> res(n, 0);
        for(int i = 0; i < n; ++i) {
            for(int j = l; j < r; ++j) {
                res[i] = safe_mod(safe_mul(res[i], base[i]) + str[i] + h);
            }
        }
        return res;
    }

    static void set_base() {
        random_number_generator_64 rnd;
        base.resize(n);
        for(int i = 0; i < n; ++i) {
            base[i] = rnd.get(0, (1UL<<61) - 1);
        }
    }
private:
    size_t sz;
    std::vector<std::vector<u64>> base_pow;
    std::vector<std::vector<u64>> hash;
public:
    static std::vector<u64> base;
};

template<int n>
std::vector<std::uint64_t> rolling_hash<n>::base {};

}


#include <algorithm>
#include <cassert>
#include <limits>
#include <queue>
#include <vector>


#include <algorithm>
#include <utility>
#include <vector>

namespace atcoder {
namespace internal {

template <class E> struct csr {
    std::vector<int> start;
    std::vector<E> elist;
    explicit csr(int n, const std::vector<std::pair<int, E>>& edges)
        : start(n + 1), elist(edges.size()) {
        for (auto e : edges) {
            start[e.first + 1]++;
        }
        for (int i = 1; i <= n; i++) {
            start[i] += start[i - 1];
        }
        auto counter = start;
        for (auto e : edges) {
            elist[counter[e.first]++] = e.second;
        }
    }
};

}  // namespace internal

}  // namespace atcoder


#include <vector>

namespace atcoder {

namespace internal {

template <class T> struct simple_queue {
    std::vector<T> payload;
    int pos = 0;
    void reserve(int n) { payload.reserve(n); }
    int size() const { return int(payload.size()) - pos; }
    bool empty() const { return pos == int(payload.size()); }
    void push(const T& t) { payload.push_back(t); }
    T& front() { return payload[pos]; }
    void clear() {
        payload.clear();
        pos = 0;
    }
    void pop() { pos++; }
};

}  // namespace internal

}  // namespace atcoder


namespace atcoder {

template <class Cap, class Cost> struct mcf_graph {
  public:
    mcf_graph() {}
    explicit mcf_graph(int n) : _n(n) {}

    int add_edge(int from, int to, Cap cap, Cost cost) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        assert(0 <= cost);
        int m = int(_edges.size());
        _edges.push_back({from, to, cap, 0, cost});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
        Cost cost;
    };

    edge get_edge(int i) {
        int m = int(_edges.size());
        assert(0 <= i && i < m);
        return _edges[i];
    }
    std::vector<edge> edges() { return _edges; }

    std::pair<Cap, Cost> flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) {
        return slope(s, t, flow_limit).back();
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t) {
        return slope(s, t, std::numeric_limits<Cap>::max());
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);

        int m = int(_edges.size());
        std::vector<int> edge_idx(m);

        auto g = [&]() {
            std::vector<int> degree(_n), redge_idx(m);
            std::vector<std::pair<int, _edge>> elist;
            elist.reserve(2 * m);
            for (int i = 0; i < m; i++) {
                auto e = _edges[i];
                edge_idx[i] = degree[e.from]++;
                redge_idx[i] = degree[e.to]++;
                elist.push_back({e.from, {e.to, -1, e.cap - e.flow, e.cost}});
                elist.push_back({e.to, {e.from, -1, e.flow, -e.cost}});
            }
            auto _g = internal::csr<_edge>(_n, elist);
            for (int i = 0; i < m; i++) {
                auto e = _edges[i];
                edge_idx[i] += _g.start[e.from];
                redge_idx[i] += _g.start[e.to];
                _g.elist[edge_idx[i]].rev = redge_idx[i];
                _g.elist[redge_idx[i]].rev = edge_idx[i];
            }
            return _g;
        }();

        auto result = slope(g, s, t, flow_limit);

        for (int i = 0; i < m; i++) {
            auto e = g.elist[edge_idx[i]];
            _edges[i].flow = _edges[i].cap - e.cap;
        }

        return result;
    }

  private:
    int _n;
    std::vector<edge> _edges;

    struct _edge {
        int to, rev;
        Cap cap;
        Cost cost;
    };

    std::vector<std::pair<Cap, Cost>> slope(internal::csr<_edge>& g,
                                            int s,
                                            int t,
                                            Cap flow_limit) {

        std::vector<std::pair<Cost, Cost>> dual_dist(_n);
        std::vector<int> prev_e(_n);
        std::vector<bool> vis(_n);
        struct Q {
            Cost key;
            int to;
            bool operator<(Q r) const { return key > r.key; }
        };
        std::vector<int> que_min;
        std::vector<Q> que;
        auto dual_ref = [&]() {
            for (int i = 0; i < _n; i++) {
                dual_dist[i].second = std::numeric_limits<Cost>::max();
            }
            std::fill(vis.begin(), vis.end(), false);
            que_min.clear();
            que.clear();

            size_t heap_r = 0;

            dual_dist[s].second = 0;
            que_min.push_back(s);
            while (!que_min.empty() || !que.empty()) {
                int v;
                if (!que_min.empty()) {
                    v = que_min.back();
                    que_min.pop_back();
                } else {
                    while (heap_r < que.size()) {
                        heap_r++;
                        std::push_heap(que.begin(), que.begin() + heap_r);
                    }
                    v = que.front().to;
                    std::pop_heap(que.begin(), que.end());
                    que.pop_back();
                    heap_r--;
                }
                if (vis[v]) continue;
                vis[v] = true;
                if (v == t) break;
                Cost dual_v = dual_dist[v].first, dist_v = dual_dist[v].second;
                for (int i = g.start[v]; i < g.start[v + 1]; i++) {
                    auto e = g.elist[i];
                    if (!e.cap) continue;
                    Cost cost = e.cost - dual_dist[e.to].first + dual_v;
                    if (dual_dist[e.to].second - dist_v > cost) {
                        Cost dist_to = dist_v + cost;
                        dual_dist[e.to].second = dist_to;
                        prev_e[e.to] = e.rev;
                        if (dist_to == dist_v) {
                            que_min.push_back(e.to);
                        } else {
                            que.push_back(Q{dist_to, e.to});
                        }
                    }
                }
            }
            if (!vis[t]) {
                return false;
            }

            for (int v = 0; v < _n; v++) {
                if (!vis[v]) continue;
                dual_dist[v].first -= dual_dist[t].second - dual_dist[v].second;
            }
            return true;
        };
        Cap flow = 0;
        Cost cost = 0, prev_cost_per_flow = -1;
        std::vector<std::pair<Cap, Cost>> result = {{Cap(0), Cost(0)}};
        while (flow < flow_limit) {
            if (!dual_ref()) break;
            Cap c = flow_limit - flow;
            for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                c = std::min(c, g.elist[g.elist[prev_e[v]].rev].cap);
            }
            for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                auto& e = g.elist[prev_e[v]];
                e.cap += c;
                g.elist[e.rev].cap -= c;
            }
            Cost d = -dual_dist[s].first;
            flow += c;
            cost += c * d;
            if (prev_cost_per_flow == d) {
                result.pop_back();
            }
            result.push_back({flow, cost});
            prev_cost_per_flow = d;
        }
        return result;
    }
};

}  // namespace atcoder


namespace ebi {

void main_() {
    int n;
    std::cin >> n;
    std::vector<std::string> s(n);
    for(int i = 0; i < n; i++) {
        std::cin >> s[i];
    }
    ebi::rolling_hash<2>::set_base();
    std::vector<ebi::rolling_hash<2>> rhs;
    for(int i = 0; i < n; i++) {
        rhs.emplace_back(ebi::rolling_hash<2>(s[i]));
    }
    std::vector table(n, std::vector<int>(n, 0));
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < n; j++) {
            if(i == j) continue;
            int sz = s[i].size();
            for(int k = sz-1; k >= 0; k--) {
                int w = sz - k;
                if((int)s[j].size() < w) break;
                if(rhs[i].get_hash(k, sz) == rhs[j].get_hash(0, w)) {
                    chmax(table[i][j], w);
                }
            }
        }
    }
    atcoder::mcf_graph<int,int> gh(2*n + 2);
    int source = 2*n, t = 2*n+1;
    rep(i,0,n) {
        gh.add_edge(source, i, 1, 0);
        gh.add_edge(i+n, t, 1, 0);
        rep(j,0,n) {
            if(i == j) {
                int sz = s[i].size();
                int ret = 0;
                rep(k,1,sz) {
                    if(rhs[i].get_hash(k, sz) == rhs[i].get_hash(0, sz-k)) {
                        ret = sz-k;
                        break;
                    }
                }
                gh.add_edge(i, j+n, 1, sz - ret);
                continue;
            }
            gh.add_edge(i, n+j, 1, s[i].size() - table[i][j]);
        }
    }
    std::cout << gh.flow(source, t).second << '\n';
}

}  // namespace ebi

int main() {
    std::cout << std::fixed << std::setprecision(15);
    std::cin.tie(nullptr);
    std::ios::sync_with_stdio(false);
    int t = 1;
    // std::cin >> t;
    while (t--) {
        ebi::main_();
    }
    return 0;
}
accuracy: 1
time_ms: 340
memory_kb: 7384
final_rank: 15

code_snippet: #line 1 "solve/main.cpp"
#include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <cctype>
#include <chrono>
#include <cmath>
#include <complex>
#include <cstdint>
#include <cstdlib>
#include <deque>
#include <functional>
#include <iomanip>
#include <iostream>
#include <limits>
#include <list>
#include <map>
#include <memory>
#include <numeric>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>

/* template start */

#line 2 "lib/utility/type_alias.hpp"

#line 4 "lib/utility/type_alias.hpp"

using u64 = std::uint64_t;
using u32 = std::uint32_t;
using u16 = std::uint16_t;
using u8 = std::uint8_t;

using i64 = std::int64_t;
using i32 = std::int32_t;
using i16 = std::int16_t;
using i8 = std::int8_t;

using usize = std::size_t;
using isize = std::ptrdiff_t;

constexpr i64 operator"" _i64(unsigned long long num) { return i64(num); }

constexpr u64 operator"" _u64(unsigned long long num) { return u64(num); }
#line 33 "solve/main.cpp"

#define rep(i, a, b) for (i64 i = (a); (i) < (b); (i)++)
#define all(i) i.begin(), i.end()

#ifdef LOCAL
#define debug(...)                                                    \
  std::cerr << "LINE: " << __LINE__ << "  [" << #__VA_ARGS__ << "]:", \
      debug_out(__VA_ARGS__)
#else
#define debug(...)
#endif

void debug_out() { std::cerr << std::endl; }

template <typename Head, typename... Tail>
void debug_out(Head h, Tail... t) {
  std::cerr << " " << h;
  if (sizeof...(t) > 0) std::cerr << " :";
  debug_out(t...);
}

template <typename T1, typename T2>
std::ostream& operator<<(std::ostream& os, std::pair<T1, T2> pa) {
  return os << pa.first << " " << pa.second;
}

template <typename T>
std::ostream& operator<<(std::ostream& os, std::vector<T> vec) {
  for (std::size_t i = 0; i < vec.size(); i++)
    os << vec[i] << (i + 1 == vec.size() ? "" : " ");
  return os;
}

template <typename T1, typename T2>
inline bool chmax(T1& a, T2 b) {
  return a < b && (a = b, true);
}

template <typename T1, typename T2>
inline bool chmin(T1& a, T2 b) {
  return a > b && (a = b, true);
}

template <typename Num, typename Func = std::multiplies<Num>>
constexpr Num mypow(Num a, u64 b, Num id = 1, Func mul = Func()) {
  if (b == 0) return id;
  Num x = id;
  while (b > 0) {
    if (b & 1) x = mul(x, a);
    a = mul(a, a);
    b >>= 1;
  }
  return x;
}

/* template end */


#include <algorithm>
#include <cassert>
#include <limits>
#include <queue>
#include <vector>

namespace atcoder {

template <class Cap, class Cost> struct mcf_graph {
  public:
    mcf_graph() {}
    mcf_graph(int n) : _n(n), g(n) {}

    int add_edge(int from, int to, Cap cap, Cost cost) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        assert(0 <= cost);
        int m = int(pos.size());
        pos.push_back({from, int(g[from].size())});
        int from_id = int(g[from].size());
        int to_id = int(g[to].size());
        if (from == to) to_id++;
        g[from].push_back(_edge{to, to_id, cap, cost});
        g[to].push_back(_edge{from, from_id, 0, -cost});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
        Cost cost;
    };

    edge get_edge(int i) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        auto _e = g[pos[i].first][pos[i].second];
        auto _re = g[_e.to][_e.rev];
        return edge{
            pos[i].first, _e.to, _e.cap + _re.cap, _re.cap, _e.cost,
        };
    }
    std::vector<edge> edges() {
        int m = int(pos.size());
        std::vector<edge> result(m);
        for (int i = 0; i < m; i++) {
            result[i] = get_edge(i);
        }
        return result;
    }

    std::pair<Cap, Cost> flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) {
        return slope(s, t, flow_limit).back();
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t) {
        return slope(s, t, std::numeric_limits<Cap>::max());
    }
    std::vector<std::pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);
        std::vector<Cost> dual(_n, 0), dist(_n);
        std::vector<int> pv(_n), pe(_n);
        std::vector<bool> vis(_n);
        auto dual_ref = [&]() {
            std::fill(dist.begin(), dist.end(),
                      std::numeric_limits<Cost>::max());
            std::fill(pv.begin(), pv.end(), -1);
            std::fill(pe.begin(), pe.end(), -1);
            std::fill(vis.begin(), vis.end(), false);
            struct Q {
                Cost key;
                int to;
                bool operator<(Q r) const { return key > r.key; }
            };
            std::priority_queue<Q> que;
            dist[s] = 0;
            que.push(Q{0, s});
            while (!que.empty()) {
                int v = que.top().to;
                que.pop();
                if (vis[v]) continue;
                vis[v] = true;
                if (v == t) break;
                for (int i = 0; i < int(g[v].size()); i++) {
                    auto e = g[v][i];
                    if (vis[e.to] || !e.cap) continue;
                    Cost cost = e.cost - dual[e.to] + dual[v];
                    if (dist[e.to] - dist[v] > cost) {
                        dist[e.to] = dist[v] + cost;
                        pv[e.to] = v;
                        pe[e.to] = i;
                        que.push(Q{dist[e.to], e.to});
                    }
                }
            }
            if (!vis[t]) {
                return false;
            }

            for (int v = 0; v < _n; v++) {
                if (!vis[v]) continue;
                dual[v] -= dist[t] - dist[v];
            }
            return true;
        };
        Cap flow = 0;
        Cost cost = 0, prev_cost_per_flow = -1;
        std::vector<std::pair<Cap, Cost>> result;
        result.push_back({flow, cost});
        while (flow < flow_limit) {
            if (!dual_ref()) break;
            Cap c = flow_limit - flow;
            for (int v = t; v != s; v = pv[v]) {
                c = std::min(c, g[pv[v]][pe[v]].cap);
            }
            for (int v = t; v != s; v = pv[v]) {
                auto& e = g[pv[v]][pe[v]];
                e.cap -= c;
                g[v][e.rev].cap += c;
            }
            Cost d = -dual[s];
            flow += c;
            cost += c * d;
            if (prev_cost_per_flow == d) {
                result.pop_back();
            }
            result.push_back({flow, cost});
            prev_cost_per_flow = d;
        }
        return result;
    }

  private:
    int _n;

    struct _edge {
        int to, rev;
        Cap cap;
        Cost cost;
    };

    std::vector<std::pair<int, int>> pos;
    std::vector<std::vector<_edge>> g;
};

}  // namespace atcoder


namespace ebi {

struct random_number_generator_64 {
private:
    using u64 = std::uint64_t;
    std::random_device rnd;
    std::mt19937_64 mt;
public:
    random_number_generator_64() : mt(rnd()) { }

    u64 get(u64 a, u64 b) {
        std::uniform_int_distribution<u64> dist(a, b-1);
        return dist(mt);
    }
};

}

namespace ebi {

template <int n>
struct rolling_hash {
 private:
  using u64 = std::uint64_t;
  const u64 mod = (1UL << 61) - 1;
  const u64 MASK31 = (1UL << 31) - 1;
  const u64 MASK30 = (1UL << 30) - 1;
  const u64 h = 100;
  const u64 buffer = mod * 4;

  u64 safe_mul(const u64& a, const u64& b) const {
    u64 au = a >> 31, ad = a & MASK31;
    u64 bu = b >> 31, bd = b & MASK31;
    u64 mid = ad * bu + au * bd;
    u64 midu = mid >> 30;
    u64 midd = mid & MASK30;
    return (au * bu * 2 + midu + (midd << 31) + ad * bd);
  }

  u64 safe_mod(const u64& a) const {
    u64 au = a >> 61;
    u64 ad = a & mod;
    u64 res = au + ad;
    if (res >= mod) res -= mod;
    return res;
  }

 public:
  rolling_hash(const std::string& s) : sz(s.size()) {
    assert(int(base.size()) == n && n > 0);
    base_pow.resize(n);
    hash.resize(n);
    for (int i = 0; i < n; ++i) {
      base_pow[i].reserve(sz + 1);
      base_pow[i].emplace_back(1);
      hash[i].reserve(sz + 1);
      hash[i].emplace_back(0);
      for (const auto& c : s) {
        hash[i].emplace_back(
            safe_mod(safe_mul(hash[i].back(), base[i]) + c + h));
        base_pow[i].emplace_back(
            safe_mod(safe_mul(base_pow[i].back(), base[i])));
      }
    }
  }

  // [l, r)

  std::vector<u64> get_hash(int l, int r) const {
    std::vector<u64> ret(n);
    for (int i = 0; i < n; ++i) {
      ret[i] = safe_mod(hash[i][r] + buffer -
                        safe_mul(hash[i][l], base_pow[i][r - l]));
    }
    return ret;
  }

  std::vector<u64> get_hash(const std::string& str, int l = 0,
                            int r = -1) const {
    if (r < 0) r = int(str.size());
    std::vector<u64> res(n, 0);
    for (int i = 0; i < n; ++i) {
      for (int j = l; j < r; ++j) {
        res[i] = safe_mod(safe_mul(res[i], base[i]) + str[i] + h);
      }
    }
    return res;
  }

  static void set_base() {
    random_number_generator_64 rnd;
    base.resize(n);
    for (int i = 0; i < n; ++i) {
      base[i] = rnd.get(0, (1UL << 61) - 1);
    }
  }

 private:
  size_t sz;
  std::vector<std::vector<u64>> base_pow;
  std::vector<std::vector<u64>> hash;

 public:
  static std::vector<u64> base;
};

template <int n>
std::vector<std::uint64_t> rolling_hash<n>::base{};

}  // namespace ebi

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

  int n;
  std::cin >> n;
  std::vector<std::string> s(n);
  for (int i = 0; i < n; i++) {
    std::cin >> s[i];
  }
  ebi::rolling_hash<2>::set_base();
  std::vector<ebi::rolling_hash<2>> rhs;
  for (int i = 0; i < n; i++) {
    rhs.emplace_back(ebi::rolling_hash<2>(s[i]));
  }
  std::vector table(n, std::vector<int>(n, 0));
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
      if (i == j) {
        int sz = s[i].size();
        for (int k = sz - 1; k >= 1; k--) {
          int w = sz - k;
          if ((int)s[j].size() < w) break;
          if (rhs[i].get_hash(k, sz) == rhs[j].get_hash(0, w)) {
            table[i][j] = std::max(table[i][j], w);
          }
        }
        continue;
      }
      int sz = s[i].size();
      for (int k = sz - 1; k >= 0; k--) {
        int w = sz - k;
        if ((int)s[j].size() < w) break;
        if (rhs[i].get_hash(k, sz) == rhs[j].get_hash(0, w)) {
          table[i][j] = std::max(table[i][j], w);
        }
      }
    }
  }

  rep(i,0,n)rep(j,0,n)debug(table[i][j]);

  atcoder::mcf_graph<int,long long> graph(2*n+2);

  const int START = 0;
  const int GOAL = 2*n+1;

  const long long TEMP = 1234;

  rep(i,0,n){
    graph.add_edge(START,1+i,1,0);
  }

  rep(i,0,n){
    rep(j,0,n){
      graph.add_edge(1+i,1+n+j,1,TEMP - table[i][j]);
    }
  }

  rep(i,0,n){
    graph.add_edge(1+n+i,GOAL,1,0);
  }

  auto ret = graph.flow(START,GOAL);

  long long ans = 0;

  rep(i,0,n)ans += s[i].size();
  ans -= TEMP * n - ret.second;

  std::cout<<ans<<"\n";

  return 0;
}
accuracy: 1
time_ms: 340
memory_kb: 7256
final_rank: 14

code_snippet: #include <bits/stdc++.h>
using namespace std;
using lint = long long;
template<class T = int> using V = vector<T>;
template<class T = int> using VV = V< V<T> >;

template<class T> struct PrimalDual {
    struct Edge { int to, rev; T cap, cost; };
    const T inf = numeric_limits<T>::max();
    const int n;
    VV<Edge> g;
    V<T> pot, dist;
    V<> pv, pe;
    PrimalDual(int n) : n(n), g(n), pot(n), dist(n), pv(n), pe(n) {}
    void add_edge(int from, int to, T cap, T cost) {
        assert(from != to);
        assert(cap >= 0);
        if (!cap) return;
        assert(cost >= 0);
        g[from].emplace_back(Edge{to, (int)g[to].size(), cap, cost});
        g[to].emplace_back(Edge{from, (int)g[from].size() - 1, 0, -cost});
    }
    void dijkstra(int s) {
        using P = pair<T, int>;
        priority_queue< P, V<P>, greater<P> > pque;
        fill(begin(dist), end(dist), inf);
        pque.emplace(dist[s] = 0, s);
        while (!pque.empty()) {
            T d; int v;
            tie(d, v) = pque.top(); pque.pop();
            if (d > dist[v]) continue;
            for (int i = 0; i < (int)g[v].size(); ++i) {
                const Edge& e = g[v][i];
                if (!e.cap or dist[e.to] <= dist[v] + e.cost - (pot[e.to] - pot[v])) continue;
                pv[e.to] = v, pe[e.to] = i;
                pque.emplace(dist[e.to] = dist[v] + e.cost - (pot[e.to] - pot[v]), e.to);
            }
        }
    }
    T min_cost_flow(int s, int t, T f) {
        assert(s != t);
        assert(f >= 0);
        T res = 0;
        fill(begin(pot), end(pot), 0);
        while (f > 0) {
            dijkstra(s);
            if (dist[t] == inf) return -1;
            for (int v = 0; v < n; ++v) pot[v] += dist[v];
            T d = f;
            for (int v = t; v != s; v = pv[v]) {
                d = min(d, g[pv[v]][pe[v]].cap);
            }
            f -= d;
            res += d * pot[t];
            for (int v = t; v != s; v = pv[v]) {
                Edge& e = g[pv[v]][pe[v]];
                e.cap -= d;
                g[v][e.rev].cap += d;
            }
        }
        return res;
    }
};

int N;
vector<string> S;

bool contain(string S, string T) {
    //cerr << S << " " << T << endl;
    string U = S + T;
    vector<int> A(U.size());
    A[0] = U.size();
    int i = 1;
    int j = 0;
    while(i < U.size()) {
        while(i + j < U.size() && U[j] == U[i+j]) ++j;
        A[i] = j;
        if(j == 0) {
            ++i;
            continue;
        }
        int k = 1;
        while(i + k < U.size() && k + A[k] < j) A[i+k] = A[k], k++;
        i += k;
        j -= k;
    }
    //cerr << S << " " << T << endl;
    //for(int i = 0; i < A.size(); i++) cerr << A[i] << " ";
    //cerr << endl;
    for(int i = S.size() + 1; i < U.size(); i++) {
        if(A[i] >= S.size()) return true;
    }
    return false;
}

int connect(string T, string S) {
    string U = S + T;
    vector<int> A(U.size());
    A[0] = U.size();
    int i = 1;
    int j = 0;
    while(i < U.size()) {
        while(i + j < U.size() && U[j] == U[i+j]) ++j;
        A[i] = j;
        if(j == 0) {
            ++i;
            continue;
        }
        int k = 1;
        while(i + k < U.size() && k + A[k] < j) A[i+k] = A[k], k++;
        i += k;
        j -= k;
    }
    //int ret = 0;
    for(int i = S.size() + 1; i < U.size(); i++) {
        int val = U.size() - i;
        if(A[i] == val) return val;
    }
    return 0;
}

int f(string S) {
    for(int i = 1; i <= S.size(); i++) {
        bool ok = true;
        for(int j = 0; i + j < S.size(); j++) {
            if(S[j] != S[j+i]) ok = false;
        }
        if(ok) return i;
    }
}

int main() {
    cin >> N;
    S.resize(N + 1);
    for(int i = 1; i <= N; i++) {
        cin >> S[i];
    }
    for(int i = 1; i <= N; i++) {
        for(int j = 1; j <= N; j++) {
            if(i == j) continue;
            if(contain(S[i], S[j])) {
                S.erase(S.begin() + i);
                N--;
                i--;
                break;
            }
        }
    }
    int source = 0;
    int target = 2 * N + 1;
    PrimalDual<int> graph(N * 2 + 2);
    for(int i = 1; i <= N; i++) {
        graph.add_edge(source, i, 1, 0);
        graph.add_edge(i, i + N, 1, f(S[i]));
        graph.add_edge(i + N, target, 1, 0);
    }
    for(int i = 1; i <= N; i++) {
        for(int j = 1; j <= N; j++) {
            if(i == j) continue;
            int val = connect(S[i], S[j]);
            graph.add_edge(i, j + N, 1, S[j].size() - val);
            //cerr << i << " " << j << " " << S[j].size() - val << endl;
        }
    }
    cout << graph.min_cost_flow(source, target, N) << endl;
    //for(int i = 1; i <= N; i++) cerr << S[i] << endl;
}
accuracy: 1
time_ms: 160
memory_kb: 3572
final_rank: 8

code_snippet: #include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
#include<set>
#include<string>
#include<queue>
#include<stack>
using namespace std;
#define MOD 1000000007
#define INF (1<<29)
#define LINF (1LL<<60)
#define EPS (1e-10)
typedef long long Int;
typedef pair<Int, Int> P;
class Edge{
public:
    int from, to, rev;
    Int cost, lim;
    Edge(int from, int to, Int cost, int lim = -1, Int rev = -1):
        from(from),to(to),cost(cost),lim(lim),rev(rev){}
};


class Graph{
public:
    int n;
    int m;
    vector<vector<Edge>> edge;
    Graph(int n = 0):n(n),m(0){
        edge = vector<vector<Edge>>(n);
    }
    void add_edge(int from, int to, int cost = 1, int lim = -1)
        {
            if(lim == -1){
                edge[from].push_back(Edge(from, to, cost));
                return;
            }
            edge[from].push_back(Edge(from, to, cost, lim, edge[to].size()));
            edge[to].push_back(Edge(to, from, -cost, 0, edge[from].size() - 1));
        }

};

class MinCostFlow{
public:
    priority_queue<P, vector<P>, greater<P> > pq;
    vector<Int> dist;
    vector<Int> before;
    vector<Int> index;
    MinCostFlow(){}
    
    void Dijkstra(Graph &g, Int s){
        fill(dist.begin(), dist.end(), INF);
        pq.push(P(0, s));
        dist[s] = 0;

        while(!pq.empty()){
            P tmp = pq.top();pq.pop();
            Int from = tmp.second;
            if(tmp.first > dist[from])continue;
            for(Int i = 0;i < g.edge[from].size();i++){
                auto e = g.edge[from][i];
                if(e.lim == 0)continue;
                if(dist[e.to] <= dist[from] + e.cost)continue;
                dist[e.to] = dist[from] + e.cost;
                before[e.to] =from;
                index[e.to] = i;
                pq.push(P(dist[e.to], e.to));
            }
        }
    }

    
    Int mcf(Graph &g, Int s, Int t, Int f){
        Int res = 0;
        dist = vector<Int>(g.n);
        before = vector<Int>(g.n);
        index = vector<Int>(g.n);
        while(f){
            Dijkstra(g, s);
            Int flow = f;
            for(Int now = t;now != s;){
                Int last = before[now];
                Int ind = index[now];
                auto &e = g.edge[last][ind];
                if(e.lim == 0 || e.to != now)return -1;
                flow = min(flow, e.lim);
                now = last;
            }
            for(Int now = t;now != s;){
                Int last = before[now];
                Int ind = index[now];
                auto &e = g.edge[last][ind];
                e.lim -= flow;
                g.edge[e.to][e.rev].lim += flow;
                res += e.cost * flow;
                now = last;
            }
            f -= flow;
            if(flow==0)return -1;
        }
        return res;
    }
};

bool included[1100];

int cycle(string &str){
    for(int i = 1;i < str.size();i++){
        bool ok = true;
        for(int j = 0;j + i < str.size();j++){
            if(str[j] != str[j+i]){
                ok = false;
                break;
            }
        }
        if(ok)return i;
    }
    return str.size();
}

bool incl(string &a, string &b){
    for(int i = 0;i + a.size() <= b.size();i++){
        bool ok = true;
        for(int j = 0;j < a.size();j++){
            if(a[j] != b[i+j]){
                ok = false;
                break;
            }
        }
        if(ok)return true;
    }
    return false;
}

int after(string &a, string &b){
    for(int i = 1;i < a.size();i++){
        bool ok = true;
        for(int j = i;j < a.size();j++){
            if(j-i >= b.size() || b[j-i] != a[j]){
                ok = false;
                break;
            }
        }
        if(ok)return i;
    }
    return a.size();
    
}

Int n, flow;
string str[110];

int main(){
    cin >> n;
    for(int i = 0;i < n;i++) cin >> str[i];

    Graph g(n * 2 + 2);
    
    Int S = n*2, T = n * 2 + 1;
    for(int i = 0;i < n;i++){
        for(int j = 0;j < n;j++){
            if(str[i].size() < str[j].size()){
                if(incl(str[i], str[j]))included[i] = true;
            }
        }
    }
    for(int i = 0;i < n;i++){
        if(included[i])continue;
        for(int j = 0;j < n;j++){
            if(included[j])continue;
            if(i == j){
                g.add_edge(i, j+n, cycle(str[i]), 1);
            }
            else{
                g.add_edge(i, j+n, after(str[i], str[j]), 1);
            }

        }
    }
    for(int i = 0;i < n;i++){
        if(included[i])continue;
        flow++;
        g.add_edge(S, i, 0, 1);
        g.add_edge(i + n, T, 0, 1);
    }
    MinCostFlow mcf;
    
    cout << mcf.mcf(g, S, T, flow) << endl;
  
    return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 3924
final_rank: 3