首先说一下,对一个刚学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. EOJ Monthly 2019.2 E. 中位数 (二分+dfs)

    题目传送门 题意: 在一个n个点,m条边的有向无环图中,求出所有从1到n 的路径的中位数的最大值 一条路径的中位数指的是:一条路径有 n 个点, 将这 n 个点的权值从小到大排序后,排在位置 ⌊n2⌋ ...

  2. Kafka DockerFile

    FROM php:5.6.38-fpm COPY . /alidata/workerspace WORKDIR /alidata/workerspace RUN set -x && a ...

  3. 关于conda-新手必读

    一.管理conda 通过anaconda来安装python及python包,让你不必关心系统是否安装了一些依赖,如zlib等等,anaconda已经集成了这些依赖,可以方便的安装python 下载请点 ...

  4. docker技术基础

    1 Linux Namespace Linux Namespaces机制提供一种资源隔离方案.PID,IPC,Network等系统资源不再是全局性的,而是属于特定的Namespace.每个Namesp ...

  5. [BJWC2010]严格次小生成树(LCA,最小生成树)

    [BJWC2010]严格次小生成树 题目描述 小C最近学了很多最小生成树的算法,Prim算法.Kurskal算法.消圈算法等等.正当小C洋洋得意之时,小P又来泼小C冷水了.小P说,让小C求出一个无向图 ...

  6. ps:点阵格式图像

    我们所看到的图像,究竟是如何构成的呢?这就需要涉及到图像类型的概念. 电脑中的图像类型分为两大类,一类称为点阵图,一类称为矢量图. 点阵图顾名思义就是由点构成的,如同用马赛克去拼贴图案一样,每个马赛克 ...

  7. 函数&&变量

    #*- encoding=utf-8 -*import sysprint(sys.getdefaultencoding()) def test(x,y,z): print(x) print(y) pr ...

  8. P1864 [NOI2009]二叉查找树

    链接P1864 [NOI2009]二叉查找树 这题还是蛮难的--是我菜. 题目描述中的一大堆其实就是在描述\(treap.\),考虑\(treap\)的一些性质: 首先不管怎么转,中序遍历是确定的,所 ...

  9. Linux系统下安装jenkins使用

    jenkins 2.190.1 yum 安装 devops一梦千年 发布时间:10-0916:28 jenkins 2.190.1 yum 安装记录 安装环境: 所需安装包: https://pkg. ...

  10. 冒泡排序算法-python

    冒泡排序:每两个相互比较,总是选出大的相互交换,直至最后选出该列表中最大的数字 def bubbleSort(myList): for i in range(len(myList)-1):#一共进行几 ...