题目传送门点我传送

Ⅰ.字典树+树型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前缀单词的更多相关文章

  1. P1666 前缀单词

    P1666 前缀单词 tire树上跑dp 首先将trie树建出来,然后对于每个节点.考虑他的子节点. 子节点的方案数都互不干扰,所以子节点与其他子节点的的方案数可以利用乘法原理算出来. 然后如果这个节 ...

  2. 洛谷 P1666 前缀单词 题解

    题意:给n个单词,如果单词a为单词b的前缀则a,b不能共存,问能共存的集合数(包括空集) 一道dp题,排序后判断,f[i][j]表示i和j是否能共存,f[i][j]=1表示能共存,初始化dp[i]=1 ...

  3. 【luogu P1666 前缀单词】 题解

    题目链接:https://www.luogu.org/problemnew/show/P1666 10.13考试题 当时没想出来,觉得是要用trie做,在trie上跑一个树形dp 结果是写了个子集枚举 ...

  4. Luogu P1666 前缀单词

    校内资格赛题目,差点高一就要\(\tt{AFO}\)了 30分思路 对30%的数据,满足$1≤n≤10 $ 所以我们可以子集枚举,实际得分40pts #include<iostream> ...

  5. 【洛谷 P1666】 前缀单词 (Trie)

    题目链接 考试时暴搜50分...其实看到"单词","前缀"这种字眼时就要想到\(Trie\)的,哎,我太蒻了. 以一个虚点为根,建一棵\(Trie\),然后\( ...

  6. [luoguP1666] 前缀单词(DP)

    传送门 先把所有字符串按照字典序排序一下 会发现有字符串x和y(x再y前面,即字典序小),如果x不是y的前缀,那么在x前面不是x前缀的字符串也不是y的前缀 这样就可以DP了 f[i][j]表示前i个字 ...

  7. [LeetCode] Word Squares 单词平方

    Given a set of words (without duplicates), find all word squares you can build from them. A sequence ...

  8. HDU 1251 字典树(前缀树)

    题目大意 :Ignatius最近遇到一个难题,老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀).(单词互不相同) ...

  9. 如何区别英语前缀pri,pro,per,pre?

    pri- 前缀pri-来源于拉丁语的这几个形容词“prim.us”, “prim.a”, “prim.um”,表示“第一的”的意思,和“pri.or”, “pri.or”, “pri.us”,是“优先 ...

随机推荐

  1. [编译] 7、在Linux下搭建安卓APP的开发烧写环境(makefile版-gradle版)—— 在Linux上用命令行+VIM开发安卓APP

    April 18, 2020 6:54 AM - BEAUTIFULZZZZ 目录 0 前言 1 gradle 安装配置 1.1 卸载系统默认装的gradle 1.2 下载对应版本的二进制文件 1.3 ...

  2. Error: opening registry key 'Software\JavaSoft\Java Runtime Environment' could not find java.dll

    Error: opening registry key 'Software\JavaSoft\Java Runtime Environment'Error: could not find java.d ...

  3. push和appendChild的区别

    概述:绑定事件(push和appendChild用法相似:但是一个是控制数组,一个是控制元素节点)用法:1.数组1的更改后的长度 = 数组1.push();//用来控制数组,在数组最后面插入项,返回数 ...

  4. Web三维编程入门总结之二:面向对象的基础Web3D框架

    本篇主要通过分析Tony Parisi的sim.js库(原版代码托管于:https://github.com/tparisi/WebGLBook/tree/master/sim),总结基础Web3D框 ...

  5. paddlehub Test on win10

    conda 构建虚拟环境 1)虚拟环境下安装paddlepaddle 1.7 2)pip install paddlehub 3)添加环境变量hub_home,以免模型把c盘撑爆 4)下载的模型在.p ...

  6. D - Romantic

    The Sky is Sprite. The Birds is Fly in the Sky. The Wind is Wonderful. Blew Throw the Trees Trees ar ...

  7. 在数组添加元素时报错:IndexError: list index out of range

    今天第一次发随笔还有许多不足之处,欢迎评论!!! 最近在写一个成语接龙的小游戏,结果在数组添加元素时报错:IndexError: list index out of range 源码: import ...

  8. 【5min+】为你的.NET应用进行一次全方位体检

    系列介绍 [五分钟的dotnet]是一个利用您的碎片化时间来学习和丰富.net知识的博文系列.它所包含了.net体系中可能会涉及到的方方面面,比如C#的小细节,AspnetCore,微服务中的.net ...

  9. Des对称可逆加密

    /// <summary> /// DES AES Blowfish ///  对称加密算法的优点是速度快, ///  缺点是密钥管理不方便,要求共享密钥. /// 可逆对称加密  密钥长 ...

  10. BMP图片解析

    本博客参考:https://www.cnblogs.com/l2rf/p/5643352.html 一.简介 BMP(Bitmap-File)图形文件是Windows采用的图形文件格式,在Window ...