P1666前缀单词
题目传送门点我传送
Ⅰ.字典树+树型DP
非常奇妙的一种解法
第一部分:构建树
先对来的单词读入,插入字典树
然后对于一颗字典树,其实是有很多无用边的,所以我们需要删去一些边
删去非单词节点和非单词节点之间的边,其实就是下面这个函数
void rebuild(int now,int fa)
{
if(isok[now])//当前节点是单词
{
vec[fa].push_back(now);//连边
fa=now;//换爸爸了
}
for(int i=1;i<=26;i++)
{
if(!tree[now][i]) continue;
rebuild(tree[now][i],fa);//递归
}
}
第二部分:树型DP
对于每一棵子树而言,右选和不选两种方案
选,则子树上的节点都不能再选,即为
\(dp[i][1]=1\)
不选,则子树上的节点可选可不选
\(f[i][0] = \prod_{j\in son[i]}(f[j][0]+f[j][1])\)
因为是计算方案,决策之间是彼此联系的,所以是相乘
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5009;
int n;
int tree[maxn][27];int isok[maxn],tot;
void insert(string s)
{
int now=0;
for(int i=0;i<s.length();i++)
{
int k=s[i]-'a'+1;
if(!tree[now][k])
tree[now][k]=++tot;//没有节点,新创建一个节点
now=tree[now][k];//去下一个节点
}
isok[now]=1;//标记单词
}
vector<int>vec[maxn];
void rebuild(int now,int fa)
{
if(isok[now])//当前节点是单词
{
vec[fa].push_back(now);//连边
fa=now;//换爸爸了
}
for(int i=1;i<=26;i++)
{
if(!tree[now][i]) continue;
rebuild(tree[now][i],fa);//递归
}
}
ll dp[maxn][3];
void ddp(int now)//开始DP
{
dp[now][1]=dp[now][0]=1;
for(int i=0;i<vec[now].size();i++)//遍历所有儿子
{
int v=vec[now][i];
ddp(v);
dp[now][0]=dp[now][0]*(dp[v][0]+dp[v][1]);//不选父节点
}
}
int main()
{
cin>>n;
string s;
for(int i=1;i<=n;i++) cin>>s,insert(s);
for(int i=1;i<=26;i++)
{
if(!tree[0][i]) continue;
rebuild(tree[0][i],0);//有结点才向下建树
}
ddp(0);//树型DP
cout<<dp[0][0];
}
Ⅱ.线性DP
我们预处理一个\(f[i][j]\)表示第i个单词与第j个单词是否能共存,
然后考虑一个\(dp[i]\)表示在前i个单词中,必须包含第i个单词的子集个数,首先\(dp[i]=1\)(只包含自己一个元素的一个子集方案数)
那么如果前面存在一个\(j<i\),并且\(f[i][j]=1\)(即i与j可以共存),那么j所有的子集方案数加上一个i元素就形成了对应新的方案,那么状态就由j转移到i了,最后,直接累计\(dp[1....n]\)即可,当然最后还要算上空集哟。
但是,这么做的前提是单词必须先排序
为什么呢??因为在\(dp[j]\)的方案中,可能有是\(a[i]\)前缀的单词,那我们就不能转移
但如果按照字典序排过之后,就不存在这种问题。
#include <bits/stdc++.h>
using namespace std;//dp[i]为必须包括i的个数
typedef long long ll;
ll vis[59][59],dp[59];
string a[59];
bool pan(int l,int r)
{
int p1=0,p2=0;
while(p1<a[l].length()&&p2<a[r].length())
if(a[l][p1++]!=a[r][p2++]) return false;
return true;
}
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i],dp[i]=1;
sort(a+1,a+1+n);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
if(pan(i,j))
vis[i][j]=1;
}
for(int i=2;i<=n;i++)
{
for(int j=1;j<i;j++)
{
if(vis[i][j]==0)//可以放一起
dp[i]+=dp[j];
}
}
ll ans=0;
for(int i=1;i<=n;i++) ans+=dp[i];
cout<<ans+1;
}
P1666前缀单词的更多相关文章
- P1666 前缀单词
P1666 前缀单词 tire树上跑dp 首先将trie树建出来,然后对于每个节点.考虑他的子节点. 子节点的方案数都互不干扰,所以子节点与其他子节点的的方案数可以利用乘法原理算出来. 然后如果这个节 ...
- 洛谷 P1666 前缀单词 题解
题意:给n个单词,如果单词a为单词b的前缀则a,b不能共存,问能共存的集合数(包括空集) 一道dp题,排序后判断,f[i][j]表示i和j是否能共存,f[i][j]=1表示能共存,初始化dp[i]=1 ...
- 【luogu P1666 前缀单词】 题解
题目链接:https://www.luogu.org/problemnew/show/P1666 10.13考试题 当时没想出来,觉得是要用trie做,在trie上跑一个树形dp 结果是写了个子集枚举 ...
- Luogu P1666 前缀单词
校内资格赛题目,差点高一就要\(\tt{AFO}\)了 30分思路 对30%的数据,满足$1≤n≤10 $ 所以我们可以子集枚举,实际得分40pts #include<iostream> ...
- 【洛谷 P1666】 前缀单词 (Trie)
题目链接 考试时暴搜50分...其实看到"单词","前缀"这种字眼时就要想到\(Trie\)的,哎,我太蒻了. 以一个虚点为根,建一棵\(Trie\),然后\( ...
- [luoguP1666] 前缀单词(DP)
传送门 先把所有字符串按照字典序排序一下 会发现有字符串x和y(x再y前面,即字典序小),如果x不是y的前缀,那么在x前面不是x前缀的字符串也不是y的前缀 这样就可以DP了 f[i][j]表示前i个字 ...
- [LeetCode] Word Squares 单词平方
Given a set of words (without duplicates), find all word squares you can build from them. A sequence ...
- HDU 1251 字典树(前缀树)
题目大意 :Ignatius最近遇到一个难题,老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀).(单词互不相同) ...
- 如何区别英语前缀pri,pro,per,pre?
pri- 前缀pri-来源于拉丁语的这几个形容词“prim.us”, “prim.a”, “prim.um”,表示“第一的”的意思,和“pri.or”, “pri.or”, “pri.us”,是“优先 ...
随机推荐
- 关于在React中 报Super expression must either be null or a function, not undefined (采坑系列)
今天突然在联系React中遇到一开始就报 Super expression must either be null or a function, not undefined 百度,各种方法,.. ...
- A Bug's Life POJ 2492
D - A Bug's Life 二分图 并查集 BackgroundProfessor Hopper is researching the sexual behavior of a rare spe ...
- Cucumber(4)——jenkins的集成
目录 回顾 必备知识 集成方法 回顾 在上几节中,关于cucumber的知识我已经全部的介绍完了,但是近期,jenkins大行其道,在工作上面能为我们节省大量的时间. 所以在本节中,我会介绍cucum ...
- awd平台搭建
1.先是使用 https://github.com/m0xiaoxi/AWD_CTF_Platform 这个平台搭建 这个平台很好用,是python脚本自动搭建,基本不需要怎么更改,自带了四道题的源码 ...
- STL入门大全(待编辑)
前言:这个暑假才接触STL,仿佛开启了新世界的大门(如同学完结构体排序一般的快乐\(≧▽≦)/),终于彻底领悟了大佬们说的“STL大法好”(虽然我真的很菜www现在只学会了一点点...)这篇blog主 ...
- 用 Python 获取百度搜索结果链接
前言 近期有许多项目需要这个功能,由于Python实现起来比较简单就这么做了,代码贴下来觉得好点个赞吧~ 代码 # coding: utf-8 import os import time import ...
- Linux必须会的命令---也是以前记录的,ctrl+z fg 啥的 jobs 比较实用
fg.bg.jobs.&.ctrl + z都是跟系统任务有关的,虽然现在基本上不怎么需要用到这些命令,但学会了也是很实用的 一.& 最经常被用到 这个用在一个命令的最后,可以把这个命令 ...
- printf 参数检查 __attribute__((format(printf, 1, 2)))
With GCC, I can specify __attribute__((format(printf, 1, 2))) , telling the compiler that this funct ...
- 深入理解TCP建立和关闭连接
建立连接: 理解:窗口和滑动窗口TCP的流量控制TCP使用窗口机制进行流量控制什么是窗口?连接建立时,各端分配一块缓冲区用来存储接收的数据,并将缓冲区的尺寸发送给另一端 接收方发送的确认信息中包含了自 ...
- JDK13的六大重要新特性
文章目录 JDK13的六大重要特性 支持Unicode 12.1 动态CDS归档(Dynamic CDS Archiving) java.net.Socket和java.net.ServerSocke ...