传送门
题意:
给你n个字符串,不同的排列有不同的代价,代价按照如下方式计算(字符串s的位置为x):
1.排在s后面的字符串有s的后缀,则代价为n^2;
2.排在s前面的字符串有s的后缀,且没有排在s后面的s的后缀,则代价为x-y(y为最后一个与s不相等的后缀的位置);
3.s没有后缀,则代价为x。
求最小代价和。
思路:显然把所有字符串倒序建一个
t
r
i
e
trie
trie树出来。
然后在上面贪心每次走
s
i
z
e
size
size最小的子树最优。
代码:
#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
const int N=1e5+5,M=510005;
typedef long long ll;
typedef pair<int,int> pii;
char s[N];
int n;
ll ans=0;
namespace Trie{
int son[M][26],siz[M],tot=1,rt=1,num[M],cnt=-1;
bool ed[M];
set<pii>e[M];
inline void insert(char s[]){
int p=rt;
for(ri i=strlen(s)-1;~i;--i){
if(!son[p][s[i]-'a'])son[p][s[i]-'a']=++tot;
p=son[p][s[i]-'a'];
++siz[p];
}
ed[p]=1;
}
void build(int p=rt,int pre=rt){
if(ed[p])e[pre].insert(pii(siz[p],p));
for(ri i=0;i<26;++i)if(son[p][i])build(son[p][i],ed[p]?p:pre);
}
void dfs(int p=rt){
num[p]=++cnt;
for(set<pii>::iterator it=e[p].begin();it!=e[p].end();++it)dfs(it->se),ans+=num[it->se]-num[p];
}
}
int main(){
scanf("%d",&n);
for(ri i=1;i<=n;++i)scanf("%s",s),Trie::insert(s);
Trie::build();
Trie::dfs();
cout<<ans;
return 0;
}