6360: 词韵
时间限制: 2 Sec 内存限制: 128 MB
提交: 168 解决: 30
[提交] [状态] [讨论版] [命题人:admin]
题目描述
Adrian 很喜欢诗歌中的韵。他认为,两个单词押韵当且仅当它们的最长公共 后缀的长度至少是其中较长单词的长度减一。也就是说,单词 A 与单词 B 押韵 当且仅当 LCS(A, B) ≥ max(|A|, |B|) – 1。(其中 LCS 是最长公共后缀 longest common suffix 的缩写)
现在,Adrian 得到了 N 个单词。他想从中选出尽可能多的单词,要求它们能 组成一个单词序列,使得单词序列中任何两个相邻单词是押韵的。
输入
第一行是一个整数N。
接下来N行,每行一个由小写英文字母组成的字符串,表示每个单词。所有单词互不相同。
输出
输出一行,为一个整数,表示最长单词序列的长度。
样例输入
5 ask psk k krafna sk
样例输出
4
提示
一种最长单词序列是 ask-psk-sk-k。
30%的测试数据:1 ≤ N ≤ 20,所有单词长度之和不超过 3 000。
100%的测试数据:1 ≤ N ≤ 500 000,所有单词长度之和不超过 3 000 000。
把串反过来建一棵 trie,然后就变成在树上找一条路径,路径中相邻两个点的 lca 与这两个点距离最多为 11,于是就变成树形 dp 了。分两种情况转移:路径一端在根节点和两端都不在根节点。
代码稍作改动
#include <bits/stdc++.h>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
#define maxx(x,y,z) max(x,max(y,z))
#define maxn 3000010
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;
}
int T, val[maxn], head[maxn], nxt[maxn], ch[maxn];
void Insert(char *S) {
int u = 1, n = strlen(S);
dwn(i, n - 1, 0)
{
int c = S[i] - '0';
bool has = 0;
for(int e = head[u]; e; e = nxt[e])
if(ch[e] == c){ has = 1; u = e; break; }
if(!has)
{
nxt[++T] = head[u]; ch[T] = c; head[u] = T;
u = T;
}
}
val[u]++;
}
int f[2][maxn];
void dp(int u) {
int cnts = 0, mx = -1, mx2 = -1;
for(int e = head[u]; e; e = nxt[e])
{
dp(e);
cnts += val[e];
int nval = f[1][e] - val[e];
if(nval > mx)
mx2 = mx, mx = nval;
else if(nval > mx2)
mx2 = nval;
}
cnts += val[u];
if(val[u])
f[1][u] = cnts + max(mx, 0);
if(mx2 >= 0)
f[0][u] = cnts + mx + mx2;
}
int n;
char S[maxn];
int main() {
T = 1;
n = read();
rep(i, 1, n) scanf("%s", S), Insert(S);
dp(1);
int ans = 0;
rep(i, 1, T) ans = maxx(ans,f[0][i],f[1][i]);///max(ans, max(f[0][i], f[1][i]));
printf("%d\n", ans);
return 0;
}