显然第一种情况可以避免
将每个串都看成树上的一个节点,父亲为它的后缀串中最长的那个
这棵树可以通过每个串reverse后加入Trie树中,最后去掉Trie树的虚节点来获得
于是问题变成了给树上每个点标号,使得每个点的标号减去它父亲的标号的和最小
显然要按DFS序标号
考虑相邻的兄弟节点u,v
先u再v比先v再u的答案大siz[u]-siz[v]
所以子节点按子树大小排序后依次标号即可
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<algorithm>
#include<map>
#include<set>
#include<stack>
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
#define mmt(a,v) memset(a,v,sizeof(a))
#define tra(i,u) for(int i=head[u];i;i=e[i].next)
using namespace std;
typedef long long ll;
const int N=510000+5;
int ch[N][26],val[N],sz;
int siz[N];
vector<int>son[N];
void insert(char *s,int k){
int u=0,n=strlen(s+1);
reverse(s+1,s+1+n);
rep(i,1,n){
int c=s[i]-'a';
if(!ch[u][c])ch[u][c]=++sz;
u=ch[u][c];
}
val[u]=k;
}
void dfs(int u,int fa){
if(val[u])son[fa].push_back(val[u]),fa=val[u];
rep(i,0,25)if(ch[u][i])dfs(ch[u][i],fa);
}
bool cmp(int i,int j){return siz[i]>siz[j];}
int dfs(int u){
siz[u]=1;
per(i,(int)son[u].size()-1,0)
siz[u]+=dfs(son[u][i]);
sort(son[u].begin(),son[u].end(),cmp);
return siz[u];
}
int id;
ll solve(int u,int fa){
ll ans=0;
ans+=id-fa;fa=id++;
per(i,(int)son[u].size()-1,0)
ans+=solve(son[u][i],fa);
return ans;
}
char s[N];
int main(){
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
int n;scanf("%d",&n);
rep(i,1,n)scanf("%s",s+1),insert(s,i);
dfs(0,0);
dfs(0);
printf("%lld\n",solve(0,0));
return 0;
}