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”,是“优先 ...
随机推荐
- 利用 Github 网络钩子实现自动化部署
GitHub 的网络钩子(webhook)功能,可以很方便的实现自动化部署.本文记录了使用 Node.js 的开发部署过程,当项目的 master 分支被推时,将在服务器进行自动部署 添加网路钩子 在 ...
- Delphi程序启动参数的读取
Delphi中有两个专门用于读取命令行参数的变量: Paramcount-->用于返回命令行参数的个数 Paramstr数组-->用于返回指定的命令行参数 示例代码 ...
- HPU第一次团队赛
D. Tom的战力问题 Tom被斯派克揍了TAT.Tom下定决心要战胜斯派克.但是在战胜最强的斯派克之前,Tom要先打败其他的狗.为此,他打算先收集一下信息.现在Tom在了得到了一些关于战斗力的小道消 ...
- frp内网穿透学习
前言 因为自己在内网,但是目标站在外网,这时候可以通过内网穿透工具,将接收到的请求转发到内网,实现在内网的msf可以控制外网的靶机. 也看了一些Ngrok,花生壳的,发现Ngrok.cc这个看文章说有 ...
- eclipse集成 json editor plugin插件
打开eclipse 找到: help--->install new software ——>add name:jsoneditor location:https://marketplace ...
- selenium 键盘鼠标模拟
一.键盘模拟常用的键 sendKeys(Keys.BACK_SPACE); //删除键--Backspace sendKeys(Keys.SPACE); //空格键 Space sendKeys ...
- Springboot:员工管理之登录、拦截器(十(4))
1:构建登录javaBean com\springboot\vo\LoginUser.java package com.springboot.vo; import lombok.Data; @Data ...
- ISWC 2018概览:知识图谱与机器学习
语义网的愿景活跃且良好,广泛应用于行业 语义网的愿景是「对计算机有意义」的数据网络(正如 Tim Berners Lee.James Hendler 和 Ora Lassila 在<科学美国人& ...
- Codeforces 1340B Nastya and Scoreboard(dp,贪心)
题目链接OvO 题目大意 给你\(n\)串数字,\(1\)代表该位置是亮的,\(0\)代表是灭的.你必须修改\(k\)个数字,使某些\(0\)变为\(1\).注意,只能把原来的\(0\)改成\(1 ...
- Java 多线程 -- volatile 山寨版的synchronized
在 多线程中,每个线程会把数据从主内存中拷贝到自己的工作内存中,当线程完成计算后,再把工作内存的数据更新到主内存中,或者当主内存主数据有更新是,线程会去主内存取最新数据.但是,当线程特别忙时,就不会去 ...