LDUOJ——前缀(字典树的链表优化)

原题链接

C. 前缀

Description
给你一个字符串集合,请从中找出一些字符串,使得找出来的这些字符串的最长公共前缀与字符串数的总个数的乘积最大化,并输出这个最大值。

Input
输入文件的第一行给出字符串个数n(1≤n≤1000000),下面n行描述这n个字符串,每个字符串长度不超过20000,输入文件大小在10MB之内

Output
输出一行一个数,表示最大化的结果

Samples
Input Copy

7
Jora de Sus
Orhei
Jora de Mijloc
Joreni
Jora de Jos
Japca
Orheiul Vechi
Output
24
Hint
数据范围 1≤n≤1000000

思路:

建立Trie树的同时记录深度和个数,相乘取max就好。
然后,卡内存,用二维数组存不行。

然而,对于本题来说这种方式并不太合适。因为本题中的字母有1000000个,在极端情况下,Trie树中结点的数量也可能到达这个数量级,字符集也没有规定,所以应该默认为256,如果按照这样一一种处理方式,所需要的内存可能达到9000MB,这显然大大超出了题中所给的限制,初始化一遍都会超时,如果把结点数开得少–些又有可能只得部分分。

然后就用链表优化吧,这里用的是链式前向星。

代码:

朴素版本:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+7;
int son[maxn][110],cnt[maxn],idx=1;
int res=0;
void add(string s){
	int p=0,dep=0;
	for(int i=0;i<s.size();i++){
		int u;
		if(s[i]==' ') u=52;
		else if(s[i]>='a'&&s[i]<='z') u=s[i]-'a';
		else u=s[i]-'A'+26;
		if(son[p][u]) p=son[p][u];
		else p=son[p][u]=++idx;
		cnt[p]++;
		res=max(res,cnt[p]*(i+1));
	}
}
int main(){
	int n;
	cin>>n;
	getchar();
	for(int i=1;i<=n;i++){
		string s;
		getline(cin,s);
		///cout<<s<<endl;
		add(s);
	}
	cout<<res<<endl; 
	return 0;
}

优化版本:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e7+7;
struct node{
	int ne,e,w;
}a[maxn];
int h[maxn],idx,cnt[maxn];
int tot=1;
void add(int u,int v,int w){
	a[idx]={h[u],v,w};h[u]=idx++;
}
int res=0;
void insert(string s){
	int p=1;
	for(int i=0;i<s.size();i++){
		int t=p;
		for(int j=h[p];~j;j=a[j].ne){
			if(a[j].w==s[i]) p=a[j].e;
		} 
		if(p==t) p=++tot,add(t,p,s[i]);
		cnt[p]++;
		res=max(res,cnt[p]*(i+1));
	}
}
int main(){
	int n;
	cin>>n;
	getchar();
	memset(h,-1,sizeof h);
	for(int i=1;i<=n;i++){
		string s;
		getline(cin,s);
		///cout<<s<<endl;
		insert(s);
	}
	cout<<res<<endl; 
	return 0;
}

然后牛客的时限给了1s,没卡过去。LDUOJ的给了2s。

原文地址:https://www.cnblogs.com/OvOq/p/14853053.html