首先说一下,对一个刚学Trie树的蒟蒻来说(就是我),这道题是一道好题。Trie树比较简单,所以就不详细写了。

Rima

内存限制:256 MiB

时间限制:1000 ms

标准输入输出

题目类型:传统

评测方式:文本比较

上传者: cqbzgm

题目描述

Adrian对单词押韵很感兴趣。如果两个单词的最长公共后缀的长度与两个单词中较长那个的长度一样,或者等于较长单词的长度减一,则这两个单词押韵。换句话说,如果A,B的最长公共后缀LCS(A,B)≥max(|A|,|B|)-1,则A和B押韵。

有一天,在阅读一套短篇小说时,他决定创造出能够使每两个相邻单词押韵的最长的单词序列,序列中的每个单词只能出现一次。但是Adrian已经厌倦了这个任务,所以他决定回去读小说,并要求你代替他解决这个任务。

输入格式

第一行输入包含整数N(1≤N≤5*1e5)。表示单词的个数。

接下来N行每行包含一个只由小写英文字母组成的字符串。表示可以用于组成序列的单词。数据保证每个单词都是不同的,保证所有单词的长度之和不超过3*1e6。

输出格式

输出一个整数。表示单词序列的最长长度。

样例

样例输入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

数据范围与提示

对于30%的数据,N≤18。

首先这道题要用到的是最长公共后缀,所以可以倒着将字符串插入字典树中以方便操作。那么我们便将样例1插入字典树中:



我们可以发现,在Tri树中,具有“押韵”关系的两个点,它们的"end"节点必定是父子或者是兄弟关系,没有例外。(比如oni和honi的end节点就是父子关系,honi和toni就是兄弟关系。)

答案已经很明显了。现在要考虑的是方法。

在考场中,我用的方法是从树的叶节点开始搜索,统计出连通的end节点的数量(这里连通的定义是具有父子关系或兄弟关系)。这样做没有问题,也很好想,但它是错的。如果我们直接暴力统计的,我们会忽略一些特殊情况(虽然这样可以骗至少40分)

这组数据:

7
ab
aab
ccb
cb
bbb
bb
b

如果使用刚才我说的直接统计每个节点在它的子树中end节点的数量(这个节点不一定是end节点,因为这样还包括两个end节点是兄弟的情况),那么它就会输出7,而正确答案却是6。

造成答案错误的原因是我们忽略了题目中的一个限制条件。在这个队列中,只有“相邻”的两个单词押韵才符合答案。



在1节点中,我们直接统计了红点的个数7个。但我们无论如何都无法将"ab","aab","ccb","cb","bbb","bb","b"这7个单词排在一起。原因是因为“b”旁边只能排有2个单词

我们可以按"aab" "ab" "b" "bb" "bbb"的顺序排列我们可以在"ab"和"b"之间插入一个"cb",这样就变成了"aab" "ab" "cb" "b" "bb" "bbb"。但"ccb"却怎么也插不进去了。

让我们回到Tri树中。使用\(f_{u}\)数组来表示一个单词它后面(注意是后面,这意味着这个单词只能接一边。而不能将其它单词接到它的前面。)的最多能接上单词的数量。

关于它的状态转移方程 :

\(f_{u}=max(f_{v}) +sum\) (\(sum\)表示u的子节点中除了f值最大的一个v节点外其它合法子节点的数量(合法意味着这个节点是end节点))

但f数组却不是最终的答案。注意我们对它的定义是在它的后面,也就是只能接上一边,所以状态转移方程不是\(f_{u}=max1(f_{v1}) +max2(f_{v2})+sum\)

(\(max1\) 和\(max2\)是第一大和第二大)

在每个节点中,定义\(ans=max1(f_{v1}) +max2(f_{v2})+sum\),答案就是最大的\(ans\).

(你们直接看代码吧,我自己也解释不清)

#include <iostream>
#include <string>
#include <cstdio>
#include <map>
#include <cstring>
using namespace std; #define N 500010
#define M 3000010
#define LL long long string A;
int n,p[M][30],k=1,ans,f[M];
bool IsEnd[M]; void Insert(string A) {
int now=1;
for(int i=A.length()-1;i>=0;i--) {
if(!p[now][A[i]-'a'])
p[now][A[i]-'a']=++k;
now=p[now][A[i]-'a'];
}
IsEnd[now]=1;
} void dfs(int x) {
int sum=0;
pair<int,int> maxx;
maxx=make_pair(0,0);
for(int i=0,v;i<26;i++)
if(p[x][i]) {
v=p[x][i];
if(IsEnd[v]) sum++;
dfs(v);
maxx=max(maxx,make_pair(maxx.first,f[v]));//这里不用pair也行,但我用其他方法
maxx=max(maxx,make_pair(f[v],maxx.first));//就WA了,所以吹爆pair
f[x]=max(f[x],f[v]);
}
if(IsEnd[x]) f[x]+=max(1,sum);
else f[x]=0;
ans=max(ans,IsEnd[x]+maxx.first+maxx.second+max(0,sum-2));
} int main() {
cin>>n;
for(int i=1;i<=n;i++) {
cin>>A;
Insert(A);
}
dfs(1);
cout<<ans;
}

校内模拟赛 : Rima —— 字典树+树形DP的更多相关文章

  1. 5.4 省选模拟赛 修改 线段树优化dp 线段树上二分

    LINK:修改 题面就不放了 大致说一下做法.不愧是dls出的题 以前没见过这种类型的 不过还是自己dp的时候写丑了. 从这道题中得到一个结论 dp方程要写的优美一点 不过写的过丑 优化都优化不了. ...

  2. codehunter 「Adera 6」杯省选模拟赛 网络升级 【树形dp】

    直接抄ppt好了--来自lyd 注意只用对根判断是否哟留下儿子 #include<iostream> #include<cstdio> using namespace std; ...

  3. 【BZOJ-2286】消耗战 虚树 + 树形DP

    2286: [Sdoi2011消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2120  Solved: 752[Submit][Status] ...

  4. 51nod 1353 树 | 树形DP经典题!

    51nod 1353 树 | 树形DP好题! 题面 切断一棵树的任意条边,这棵树会变成一棵森林. 现要求森林中每棵树的节点个数不小于k,求有多少种切法. 数据范围:\(n \le 2000\). 题解 ...

  5. 【BZOJ-3572】世界树 虚树 + 树形DP

    3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1084  Solved: 611[Submit][Status ...

  6. bzoj 2286(虚树+树形dp) 虚树模板

    树链求并又不会写,学了一发虚树,再也不虚啦~ 2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 5002  Sol ...

  7. 洛谷 P1453 城市环路 ( 基环树树形dp )

    题目链接 题目背景 一座城市,往往会被人们划分为几个区域,例如住宅区.商业区.工业区等等.B市就被分为了以下的两个区域--城市中心和城市郊区.在着这两个区域的中间是一条围绕B市的环路,环路之内便是B市 ...

  8. BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP+树剖lca

    BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的 ...

  9. [10.12模拟赛] 老大 (二分/树的直径/树形dp)

    [10.12模拟赛] 老大 题目描述 因为 OB 今年拿下 4 块金牌,学校赞助扩建劳模办公室为劳模办公室群,为了体现 OI 的特色,办公室群被设计成了树形(n 个点 n − 1 条边的无向连通图), ...

随机推荐

  1. [SP3267]DQUERY - D query

    题目传送门 维护一个区间内不同数的个数,最直观的想法是直接排序后用树状数组维护即可.但是我们发现n只有3e4,于是我们想到了可以拿一个$O(n\sqrt{n})$的莫队维护.关于莫队算法如果有不知道的 ...

  2. 【题解】Shortest Cycle

    原题链接:CF1205B   题目大意   给定\(n\)个整数\(a_1,a_2,a_3, \dots ,a_n\),若\(i \neq j\)且\(a_i \land a_j \neq 0\),则 ...

  3. Excel VBA 判断工作表是否为空或被使用过(比如设置过框线)

    IsEmpty 函数 返回 Boolean 值,指出变量是否已经初始化. [语法] IsEmpty(expression) 必要的 expression 参数是一个 Variant,包含一个数值或字符 ...

  4. Mysql 数据库存储的原理??

    储存过程是一个可编程的函数,它在数据库中创建并保存.它可以有 SQL 语句和一些特殊的控制结 构组成.当希望在不同的应用程序或平台上执行相同的函数,或者封装特定功能时,存储过程是非常有用的.数据库中的 ...

  5. c# 实现ComboBox自动模糊匹配

    ComboBox自带有属性可以实现自动匹配,但是它有一个弊端,只能从头开始匹配,例如"李四LS",只能输入“李四”或"李"才能匹配出来,而输入"LS& ...

  6. FFmpeg从入门到出家(FLV文件结构解析)

    FLV(FLASH VIDEO),是一种常用的文件封装格式,目前国内外大部分视频分享网站都是采用的这种格式.其标准定义为<Adobe Flash Video File Format Specif ...

  7. mybatis对java自定义注解的使用

    转自:https://www.cnblogs.com/sonofelice/p/4980161.html 最近在学习spring和ibatis框架. 以前在天猫实习时做过的一个小项目用到的mybati ...

  8. [转]使用flask实现mock server

    什么是mock server: http://www.testclass.net/interface/mock_server 使用flask 实现  mock server : http://www. ...

  9. 通过busybox制作根文件系统

    通过busybox制作根文件系统可以自定义选项,在制作的根文件系统中添加需要的命令,指定生成的根文件系统到相应的目录下. 一. 根文件系统的获取方式--->官网: https://busybox ...

  10. K8S进入容器方法

    前言 k8s如何进入一个pod里有多个容器的方法 参考地址 https://blog.csdn.net/aa1215018028/article/details/81205691 方法1 kubect ...