#NOIP模拟赛#押韵rhyme(TRI树 + Dp)

押韵(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
#include
#include
#include
#include
using namespace std;

const int sumMax = 3000000;

struct node{
    map 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;
}




你可能感兴趣的:(模拟赛,树)