题目传送门点我传送

Ⅰ.字典树+树型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. c++存储区

    全局变量与静态变量区.常量区.局部变量区(栈).动态存储区(堆).自由存储区 1.全局变量与静态变量区->存放全局变量.静态变量,程序运行结束后释放 2.常量区->存放常量 3.局部变量区 ...

  2. C#多线程系列(3):原子操作

    本章主要讲述多线程竞争下的原子操作. 目录 知识点 竞争条件 线程同步 CPU时间片和上下文切换 阻塞 内核模式和用户模式 Interlocked 类 1,出现问题 2,Interlocked.Inc ...

  3. 如何提高你使用windows的逼格(windows用成Linux的赶脚)

    一.准备工作 作为一个整洁而有内涵的人,电脑桌面一定要清洁 二.桌面整洁了,软件怎么打开呢?     方案一 方案二.敲重点   我们可以使用终端指令打开windows安装的任意软件: 打开Windo ...

  4. AJ学IOS 之二维码学习,快速生成二维码

    AJ分享,必须精品 二维码是一项项目中可能会用到的,iOS打开相机索取二维码的速度可不是Android能比的...(Android扫描二维码要来回来回晃...) 简单不多说,如何把一段资料(网址呀,字 ...

  5. 选择IT行业的自我心得,希望能帮助到各位!(三)失败篇

    可能很多小伙伴会说人人创业岂不是人人都能成功,岂不是人人都能成功,是不是每个人都能开上保时捷,法拉利泡着美女,很多人也会说你看他看她多轻松,做个IT一样就赚钱赚钱了. 那么又有多少人能理解到你的心酸了 ...

  6. mac上安装htop

    对于经常在mac上使用top命令的童鞋来说,htop会是一个更加好看和更加好用的命令,下面就是在mac上安装htop的步骤 1.首先去htop官网去下载,我下的是最新的2.2.0版本,网址是https ...

  7. Springboot:员工管理之登录、拦截器(十(4))

    1:构建登录javaBean com\springboot\vo\LoginUser.java package com.springboot.vo; import lombok.Data; @Data ...

  8. java 方法的重载、重写与重构

    首先我们要知道重载.重写.重构的区别 重载:指的是在同一个类中,方法名相同,但是参数数量.参数类型或者返回类型不同的方法就叫做重载. 重写: 重写分两种.第一种的是在子类继承父类的情况下,通过@Ove ...

  9. pytorch 手写数字识别项目 增量式训练

    dataset.py ''' 准备数据集 ''' import torch from torch.utils.data import DataLoader from torchvision.datas ...

  10. 科学计算包Numpy

    Numpy 用于科学计算的python模块,提供了Python中没有的数组对象,支持N维数组运算.处理大型矩阵.成熟的广播函数库.矢量运算.线性代数.傅里叶变换以及随机数生成等功能,并可与C++.FO ...