6360: 词韵(字典树,DP)

6360: 词韵

时间限制: 2 Sec  内存限制: 128 MB
提交: 171  解决: 31
[提交] [状态] [讨论版] [命题人: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。

 

 

使用字典树解决问题,首先将所有单词逆序并建立字典树,标记单词结尾结点。

在字典树上找一条最长的路径,满足 ①只走标记结点 ②从结点u可以走向父结点、孩子结点、兄弟结点。

tdp[i] :以 i 为根节点时,从任一子树走上来,走到 i 时的最长路径

 dp[i] :以i为根节点时,连接两个最长fdp[]的子树,把所有孩子连上最长路径。

dfs回溯过程中更新这两个数组,最后取dp[i],tdp[i]中的最大值

//Sinhaeng Hhjian
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<stdlib.h>
#include<stack>
#include<math.h>
#include<set>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
const int maxn = 3e6+5;
int head[maxn], v[maxn], tdp[maxn], dp[maxn], cnt, ans; 
char s[maxn];
struct node{	char to;	int next;	}edge[maxn];
int build(){
    int len=strlen(s), ss=0; 
    for(int i=0;i<len;i++){
        int flag=1;
        for(int j=head[ss];j!=-1;j=edge[j].next)
            if(edge[j].to == s[i]){
                flag=0; ss=j; break;
            }
        if(flag){
			edge[++cnt].to=s[i];
    		edge[cnt].next=head[ss];
    		head[ss]=cnt;
            ss=cnt; 
        }
    }
	v[ss]++; 
}
int dfs(int u){
    int m1=0, m2=0, sum=0;
    for(int i=head[u];i!=-1;i=edge[i].next){
        dfs(i);
        int maxx=tdp[i]-v[i]; 
        if(maxx>m2){
			m1=m2;
			m2=maxx;
		}
        else if(maxx>m1)
			m1=maxx;
        sum+=v[i];
    }
    sum+=v[u];
    if(m1>0)
		dp[u]=sum+m1+m2; 
    if(v[u])
		tdp[u]=sum+m2; 
}
 
int main()
{
    memset(head,-1,sizeof(head));
    memset(tdp, 0, sizeof(tdp));
	memset(dp, 0, sizeof(dp));
    memset(v, 0, sizeof(v));
	cnt=0;ans=0;
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        scanf("%s", s);
        int len=strlen(s);
        for(int i=0;i<len/2;i++){
        	char temp=s[i];
        	s[i]=s[len-1-i];
        	s[len-1-i]=temp;
		}
        build();
    }
    dfs(0);
    for(int i=0;i<=cnt;i++)
		ans=max(ans, max(dp[i], tdp[i]));
    cout<<ans<<endl;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hhjian6666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值