题解:P7537 [COCI2016-2017#4] Rima
liudonghao · · 题解
考虑翻转每一个字符串,这样公共后缀就变成了公共前缀。
公共前缀?建出字典树来。
(点权代表这个点有没有单词结尾)
那么,两个单词押韵,当且仅当它们的尾部节点在字典树上有父子或兄弟关系。
也就是说,序列中每一个单词的下一个单词可以是它的任意一个非空兄弟、非空父亲或非空儿子上。
一颗子树上,能遍历到最多点的情况应该长成下面这个样子:
但注意,这种情况无法对更大的子树做出贡献,因为一旦下去就再也上不来了。
所以,不仅要记录能遍历到最多点(即深入两根枝条)的情况,也要记录只深入了一根枝条的最多点数。
还有,根节点也可能没有对应的单词。
所以,设
设
当
当
上面的一遍深搜
若
答案可能是任意
代码:
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 3e6 + 10;
int m,to[N][30],val[N],n,siz[N][2][3];
void Insert(string s)
{
int id = 0;
for(char c : s)
{
if(to[id][c - 'a' + 1]) id = to[id][c - 'a' + 1];
else
{
n++;
to[id][c - 'a' + 1] = n;
id = n;
}
}
val[id]++;
}
void dfs(int u)
{
// cout << u << endl;
int maxx = -1,maxxx = -1,s = 0;
for(int i = 1; i <= 26; i++)
{
if(to[u][i])
{
dfs(to[u][i]);
if(val[to[u][i]])
{
s++;
if(siz[to[u][i]][1][1] > maxx) maxxx = maxx, maxx = siz[to[u][i]][1][1];
else if(siz[to[u][i]][1][1] > maxxx) maxxx = siz[to[u][i]][1][1];
}
}
}
if(maxxx != -1) siz[u][0][2] += maxx + maxxx;
if(maxx != -1) siz[u][0][1] += maxx;
if(s >= 1) siz[u][0][1] += s - 1;
if(s >= 2) siz[u][0][2] += s - 2;
if(val[u]) siz[u][1][2] = siz[u][0][2] + 1, siz[u][1][1] = siz[u][0][1] + 1;
}
int main()
{
cin >> m;
for(int i = 1; i <= m; i++)
{
string s; cin >> s;
reverse(s.begin(),s.end());
Insert(s);
}
dfs(0);
int ans = -1;
for(int i = 0; i <= n; i++) ans = max(ans,max(siz[i][0][2],siz[i][1][2]));
for(int i = 0; i <= n; i++) ans = max(ans,max(siz[i][0][1],siz[i][1][1]));
cout << ans << endl;
return 0;
}
//ctj可耻!