押韵(rhyme.cpp/c/pas)
【题目描述】
LCS表示最长公共后缀长度。如果两个单词A,B押韵,当且仅当LCS(A,B)>=MAX(A,B)-1。如果一个序列押韵,当且仅当该序列中任意相邻的两个单词押韵。现在,给你一片文章,文章中没有相同的两个单词。请你从该文章中选择任意单词,并任意排列顺序,得到一个尽量长的押韵序列。注意,每个单词只能出现一次。
【输入格式】
第一行一个整数N(1<=N<=500000)
接下来N行,每行一个小写字母单词。所有单词的总长度不超过3000000.
【输出格式】
输出最长的押韵序列的单词个数。
【输入样例1】
4
honi
toni
oni
ovi
【输出样例1】
3
【输入样例2】
5
ask
psk
krafna
sk
k
【输出样例2】
4
【输入样例3】
5
pas
kompas
stas
s
Nemarime
【输出样例3】
1
样例2解释:唯一的押韵序列是 ask-psk-sk-k
样例3解释:没有两个单词押韵,所以输出1.
内存:256M
时间:1s
按照从后到前建TRI树,那么一个单词就能和它的父亲,它的兄弟相邻。
对于一个有单词结尾的节点i,可以将它的子树中的最长链放在左边,中间放它的兄弟,右边放子树中的次长链,这样就在i节点拼出了一个对于i节点的最优解。
定义f[i]表示以i为结尾单词的最长链,g[i]表示次长链。
f[i] = max(f[son] + sz[son] + isword[son] - 1);
g[i] = min(f[i], f[son] + sz[son] + isword[son] - 1);
答案就是将每个i计算一下最优解,选最优的。
注意这题内存限制很严,如果直接开结构体用指针预留26个位置会爆内存,所以选择set或map来维护并快速查找位置,也可以在外面开数组用整体的链式前向星存。
Code:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<map>
using namespace std;
const int sumMax = 3000000;
struct node{
map<char, int> v;
int fa;
bool tg;
}TRI[sumMax + 5];
int N, cnt, Ans;
int sz[sumMax + 5], f[sumMax + 5], g[sumMax + 5];
char S[sumMax + 5];
void Init(){
int len = strlen(S + 1);
int r = 0;
for(int p = len; p; -- p){
if(TRI[r].v.count(S[p]) == 0){
++ cnt;
TRI[r].v.insert(make_pair(S[p], cnt));
TRI[cnt].fa = r;
}
r = TRI[r].v[S[p]];
}
TRI[r].tg = 1;
++ sz[TRI[r].fa];
}
int main(){
freopen("rhyme.in", "r", stdin);
freopen("rhyme.out", "w", stdout);
scanf("%d", &N);
for(int i = 1; i <= N; ++ i){
scanf("%s", S + 1);
Init();
}
for(int i = cnt; i >= 0; -- i) f[i] = g[i] = 1;
for(int i = cnt; i >= 0; -- i) if(TRI[i].tg){
if(f[i]+sz[i]+TRI[i].tg-1 >= f[TRI[i].fa])
g[TRI[i].fa] = f[TRI[i].fa],
f[TRI[i].fa] = f[i]+sz[i]+TRI[i].tg-1;
else if(f[i]+sz[i]+TRI[i].tg-1 > g[TRI[i].fa])
g[TRI[i].fa] = f[i]+sz[i]+TRI[i].tg-1;
}
for(int i = cnt; i >= 0; -- i)
Ans = max(Ans, f[i] + g[i] + sz[i] + TRI[i].tg - 2);
printf("%d\n", Ans);
return 0;
}


被折叠的 条评论
为什么被折叠?



