#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<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;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章