【bzoj1174】[Balkan2007]Toponyms

zz:https://www.cnblogs.com/GXZlegend/p/7134233.html

题目描述

给你一个字符集合,你从其中找出一些字符串出来. 希望你找出来的这些字符串的最长公共前缀*字符串的总个数最大化.

输入

第一行给出数字N.N在[2,1000000] 下面N行描述这些字符串,长度不超过20000 。保证输入文件不超过10MB

输出

a single line with an integer representing the maximal level of complexity Lc(T).

样例输入

7
Jora de Sus
Orhei
Jora de Mijloc
Joreni
Jora de Jos
Japca
Orheiul Vechi

样例输出

24

题解

Trie树

很显然建立Trie树,用 每个节点的深度*对应字符串个数 更新答案。

但是本题卡空间只有128M,不能使用普通的Trie树存边方式,必须使用邻接表(链式前向星)存边。

所以每次插入时扫一遍,看能否扫到该字符,并决定是否添加新边。

时间复杂度O(53len),卡卡常数可以过。另外数组大小已实测。

#include <iostream> 
#include <cstdio> 
#include <cstdlib> 
#include <cstring> 
#include <cctype> 
#include <algorithm> 
#include <vector> 
using namespace std; 
int read() 
{ 
  int x = 0, f = 1; 
  char c = getchar(); 
  while(!isdigit(c))
  {   
     if(c == '-') 
        f = -1; 
     c = getchar(); 
  } 
while(isdigit(c))
  {   
        x = x * 10 + c - '0'; 
        c = getchar(); 
  }     
return x * f; 
} 
#define maxn 5000010 
#define LL long long 
int n, rt, ToT, m, val[maxn], head[maxn], nxt[maxn], to[maxn]; 
char ec[maxn]; 
LL ans; 
void insert() 
{ 
   int u = rt; 
   val[u]++; 
   char C = getchar(); 
   for(int i = 0; C != '
'; i++, C = getchar()) 
   {    
       int v = -1; 
       for(int e = head[u]; e; e = nxt[e]) 
           if(ec[e] == C) //看是否能找到一个C这样一个子结点 
              { 
                 v = to[e]; 
                 break; 
              }  
       if(v < 0) //如果没找到则新加一个 
       {
          to[++m] = ++ToT;
          ec[m] = C;
          nxt[m] = head[u];
          head[u] = m;
          v = ToT; 
       }
       val[u = v]++; 
       ans = max(ans, (LL)val[u] * (i + 1)); 
   } 
   return ; 
} 
int main() 
{ 
n = read(); 
rt = ToT = 1; 
for(int i = 1; i <= n; i++) 
    insert(); 
printf("%lld
", ans); 
return 0; 
}