- 【题目地址】
luogu也有
题意的话就看题面吧。
我们一步一步的来分析:
首先吃最少的泡椒,那么显然可以贪心,由于 n × n n\times n n×n贡献的肯定比后面的方式都大,所以我们考虑将一个串它的所有存在的后缀串全部先放在前面,这时就不会用第一种了,然后我们考虑,可以将这种关系用边连起来,就成了一棵树,我们可以举个例子来看:
5
a
b
ba
bb
bba
这个例子便可以连出这样的一张图:
我们发现,如果先放 a a a这边的,那么最后就会是:
a - 1
ba - 1
bba - 1
b - 4
bb - 1
cost = 8
但是我们明显可以发现如果先放 b b b这边的,那么当到 a a a时,它只会有 3 3 3的代价,而其他都是 1 1 1的代价,所以如下这样会更优秀:
b - 1
bb - 1
a - 3
ba - 1
bba - 1
cost = 7
那么贪心的考虑,也就是这棵树上,我们按照子树大小,从小到大的遍历会更优秀一些。
所以一种方法是用Hash算法,在 O ( n 2 ) O(n^2) O(n2)的时间内建出这个树,然后遍历一遍排个序就可以出答案了。
目前瓶颈就在于建树,我们可以发现它的每个都是和后缀有关,但是直接建一棵广义后缀自动机太麻烦了,所以我们将其翻转后,后缀就变成了前缀,那么只需插入到一棵trie(字典)树中即可,对于一个串是另一个串的前缀,那么在字典树上它就是另一个的祖先(在另一个同一条链的上面),然后遍历一遍即可建出原树。
复杂度变成 O ( ∣ l e n ∣ ∗ 26 + n ) O(|len|*26+n) O(∣len∣∗26+n)就可以过了。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const int N=1e5+5,M=6e5+1;
int n,bef[N];
char str[M];
ll ans;
int son[M][26],f[N],flag[M],sze[N],bel[M],tot=1;
struct ss{
int to,last;
ss(){}
ss(int a,int b):to(a),last(b){}
}g[M];
int head[N],cnt;
void add(int a,int b){g[++cnt]=ss(b,head[a]);head[a]=cnt;f[b]=a;}
void addin(char *s,int k){
int id,now=1;
int len=strlen(s);
for(int i=len-1;i>=0;i--){
id=s[i]-'a';
if(!son[now][id]) son[now][id]=++tot;
now=son[now][id];
}
bel[now]=k;//建trie树,记录是哪个字符串
}
struct node{
int sze,id;
node(){}
node(int a,int b):sze(a),id(b){}
bool operator <(node a)const{
return sze<a.sze||(sze==a.sze&&id<a.id);
}//按子树大小排序
};
vector <node> vec[N];
void dfs(int a,int fa){
if(bel[a]) add(fa,bel[a]);
//记录是从fa那个串来的
for(int i=0;i<26;i++){
if(son[a][i]) dfs(son[a][i],bel[a]?bel[a]:fa);
}//建树
}
void find(int a){
sze[a]=1;
for(int i=head[a];i;i=g[i].last){
find(g[i].to);
vec[a].push_back(node(sze[g[i].to],g[i].to));
sze[a]+=sze[g[i].to];
}
sort(vec[a].begin(),vec[a].end());//排序
}
int rak;
void get(int a){
if(a) bef[a]=++rak;
for(int i=0;i<vec[a].size();i++) get(vec[a][i].id);//遍历顺序,打上时间戳
}
void file(){
freopen("word.in","r",stdin);
freopen("word.out","w",stdout);
}
void close(){
fclose(stdin);
fclose(stdout);
}
int main(){
file();
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",str);
addin(str,i);
}
dfs(1,0);
find(0);
get(0);
for(int i=1;i<=n;i++) ans+=1ll*(bef[i]-bef[f[i]]);//统计答案
printf("%lld\n",ans);
close();
return 0;
}