6360: 词韵(2018北京冬令营1 )

6360: 词韵

时间限制: 2 Sec  内存限制: 128 MB

题目描述

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。

 

题解:根据字符串倒序建字典树,然后树上dp, 若某个点是单词的结尾则 他和他父亲,孩子,以及兄弟 能形成押韵,维护一个最长押韵序列f[i],和最短的押韵序列g[i]。

 

以i为根的子树中形成的最长押韵序列长度ans=max(f[i]+g[i]+i.sons-2+(i是否带有结束标记?1 : 0)),即dfs,回溯回去

 

参考链接 12

c++ code:

#include <bits/stdc++.h>
 
using namespace std;
const int N = 3e6 + 10;
struct Node{
    int id,next;
}trie[N];
int tot,head[N],isw[N];
int dp[N][2]; 
char str[N];
void init()
{
    memset(head,-1,sizeof(head));
    memset(isw,0,sizeof(isw));
    tot = 1;
}
void add(int u,int v)
{
    trie[++tot].id = v;trie[tot].next = head[u];head[u] = tot;
}
 
void build()
{
    int u = 1, len = strlen(str);
    for(int i = len-1;i >= 0;i--)
    {
        int id = str[i] - 'a';int flag = 0;
        for(int j = head[u]; ~j; j = trie[j].next) if(trie[j].id == id) {flag = 1;u = j;break;}
        if(!flag) {add(u,id);u = tot;}
    }
    isw[u] = 1;
}
 
void dfs(int u)
{
    int max1 = -1,max2 = -1; // 最长 次长
    int sum = isw[u];
    for(int i = head[u];~i;i = trie[i].next)
    {
        dfs(i);
        sum += isw[i];
        int t = dp[i][1] - isw[i];
        if(t >= max1)
        {
            max2 = max1;
            max1 = t;
        }
        else if(t > max2)
            max2 = t;
    }
    if(max1 >= 0 && max2 >= 0)    dp[u][0] = sum + max1 + max2;
    if(isw[u])  dp[u][1] = sum + max(max1,0);
}
int main()
{
    int n;
    init();
    scanf("%d",&n);
    while(n--)
    {
        scanf("%s",str);
        build();
    }
    dfs(1);
    int ans = 0;
    for(int i = 1;i <= tot;i++)
    {
        ans = max(ans,max(dp[i][0],dp[i][1]));
       // cout<<ans<<" "<<i<<endl;
    }
    printf("%d\n",ans);
    return 0;
}

 

 

posted @ 2018-07-31 15:28  jadelemon  阅读(357)  评论(0编辑  收藏  举报