题目描述

给定一个由前\(n\)个小写字母组成的串\(S\)。

串\(S\)是阶乘字符串当且仅当前\(n\)个小写字母的全排列(共\(n!\)种)都作为\(S\)的子序列(可以不连续)出现。

由这个定义出发,可以得到一个简单的枚举法去验证,但是它实在太慢了。所以现在请你设计一个算法,在\(1\)秒内判断出给定的串是否是阶乘字符串。

输入格式

输入第\(1\)行一个整数\(T\),表示这个文件中会有\(T\)组数据。

接下来分\(T\)个块,每块\(2\)行:

第\(1\)行一个正整数\(n\),表示\(S\)由前\(n\)个小写字母组成。

第\(2\)行一个字符串\(S\)。

输出格式

对于每组数据,分别输出一行。每行是\(YES\)或者\(NO\),表示该数据对应的串\(S\)是否是阶乘字符串。

样例输入

2
2
bbaa
2
aba

样例输出

NO
YES

【样例解释】

第一组数据中,ab这个串没有作为子序列出现。

数据范围

\[N \leq 26 \\\\
T \leq 5 \\\\
|S| \leq 450 \\\\
\]

解题报告

题意理解

这道题目,有一点点绕? 可能是我太菜了

我们初步读题可知,题目要求我们判断一个字符串.

给你一个\(N\),如果说一个字符串满足\(N\)的全排列字符串

而且这些字符串,都以序列的形式出现在这个字符串,那么我们称之为合法,否则不合法.

算法解析

这道题目运用的是.状态压缩DP.

首先我们思考一下,这道题目\(N \leq 26\),这个数据范围似乎不太好状态压缩?

数据太大了....

但是我们发现,其实\(N \ge 21\),完全可以判断无解.

这是为什么,有证明吗?

当\(n \ge 21\)的时候

假设\(|S| = 450\)

在|S|中任意取21个数字

$ C(450, 21) < 21!$

说明这\(450\)个字符不能完全凑成\(n!\)个序列。


接下来我们着重分析一下,状态压缩思想.

我们知道状态压缩其实就是 集合二进制枚举 处理.

那么既然如此,我们不妨设置一下 状态表示.

  1. 状态是一个 集合
  2. 题目要求 全排列合法
  3. 一般题目中,总会有 最后一位,也就是转移过来的元素

设\(f[S]\)表示当\(S\)中集合中的字母构成的排列均在原序列\([1,f[S]]\)出现的最小值。

既然如此的话.

我们不妨预处理一下.

\(g[i][j]\)表示从\(i\)开始下一个字母\(j\)出现的位置。

总而言之,我们就是利用 刷表法则,一步步推导状态.

因此我们不妨设置核心程序.

for(int S=1; S<(1<<n); S++)//枚举子集
{
int cnt=0;
for(int i=0; i<n; i++)
if(S & (1<<i) ) //s集合拥有这一位,其实也就是i结尾
cnt=max(cnt,g[f [S^(1<<i) ]][i] );//排除这一位,然后转移过来
f[S]=cnt;//更新
}

代码解析

#include <bits/stdc++.h>
using namespace std;
const int N=460;
#define read(x) scanf("%d",&x)
int t,n,m,g[N][32],f[1<<21];
char s[N];
inline void init()
{
read(t);
while(t--)
{
read(n);
scanf("%s",s+1);//默认读入从1开始
m=strlen(s+1);
if (n>21)//特殊判定无解情况
{
puts("NO");
continue;
}
for(int i=m+1; i>=0; i--)//从i开始下一个j出现的位置
{
for(int j=0; j<n; j++)
g[i][j]=( i>=m ? m+1 : g[i+1][j] ); //前面的位置,最近的是当前这位的
if(i!=m)
g[i][ s[i+1]-'a' ]=i;//当前位为最近的
}
for(int S=1; S<(1<<n); S++)//枚举子集
{
int cnt=0;
for(int i=0; i<n; i++)
if(S & (1<<i) ) //s集合拥有这一位,其实也就是i结尾
cnt=max(cnt,g[f [S^(1<<i) ]][i] );//排除这一位,然后转移过来
f[S]=cnt;//更新
}
printf("%s\n",f[ (1<<n)-1 ] <=m ? "YES":"NO" );//是否存在
}
}
int main()
{
init();
return 0;
}

[SHOI2013]阶乘字符串的更多相关文章

  1. 洛谷 P3989 [SHOI2013]阶乘字符串 解题报告

    P3989 [SHOI2013]阶乘字符串 题目描述 给定一个由前\(n(\le 26)\)个小写字母组成的串\(S(|S|\le 450)\).串\(S\)是阶乘字符串当且仅当前 \(n\) 个小写 ...

  2. BZOJ4416: [Shoi2013]阶乘字符串

    可以大胆猜想n>21时无解,至于依据,不开O2,1s,n<=21刚好能卡过去= = 并不会证= = #include<cstdio> void up(int& a,in ...

  3. BZOJ4416 [Shoi2013]阶乘字符串 【序列自动机 + 状压dp】

    题目链接 BZOJ4416 题解 建立序列自动机,即预处理数组\(nxt[i][j]\)表示\(i\)位置之后下一个\(j\)出现的位置 设\(f[i]\)表示合法字符集合为\(i\)的最短前缀,枚举 ...

  4. [BZOJ4416][SHOI2013]阶乘字符串(子集DP)

    怎么也没想到是子集DP,想到了应该就没什么难度了. 首先n>21时必定为NO. g[i][j]表示位置i后的第一个字母j在哪个位置,n*21求出. f[S]表示S的所有全排列子序列出现的最后末尾 ...

  5. BZOJ4416 SHOI2013阶乘字符串(状压dp)

    当n大到一定程度(>21)时一定无解,并不会证. 如果要取出一个排列,显然应该让每一位在序列中的位置尽量靠前.于是设f[S]表示存在S子集中这些字母所组成的所有排列的最短前缀的长度,枚举当前排列 ...

  6. BZOJ 4416 【SHOI2013】 阶乘字符串

    题目链接:阶乘字符串 又是一道不会做的题……看了题解后我被吓傻了…… 首先我们可以有一个显然的\(O(2^nn)\)的做法.我们先预处理出\(g_{i,j}\)表示字符串中\(i\)号位置开始第一个\ ...

  7. 【JZOJ3293】【BZOJ4416】【luoguP3989】阶乘字符串

    description 给定一个由前n个小写字母组成的串S. 串S是阶乘字符串当且仅当前n个小写字母的全排列(共n!种)都作为S的子序列(可以不连续)出现. 由这个定义出发,可以得到一个简单的枚举法去 ...

  8. [JZOJ3293] 【SHTSC2013】阶乘字符串

    题目 题目大意 给你一个字符串,判断这个字符串是否为"阶乘字符串". 就是子序列包含字符集的全排列的字符串. n≤26n\leq 26n≤26 ∣S∣≤450|S|\leq 450 ...

  9. [暑假的bzoj刷水记录]

    (这篇我就不信有网站来扣) 这个暑假打算刷刷题啥的 但是写博客好累啊  堆一起算了 隔一段更新一下.  7月27号之前刷的的就不写了 , 写的累 代码不贴了,可以找我要啊.. 2017.8.27upd ...

随机推荐

  1. 客户端热更新框架之UI热更框架设计(下)

    上一篇笔者介绍了关于什么是热更新,为什么需要热更新的技术文章.本篇就专门针对UI框架的热更新功能实现部分展开讨论,讨论的重点是热更新如何与UI框架进行结合? 现在笔者把设计“UI热更新框架”的整体设计 ...

  2. window环境下zookeeper的安装(自用---仅供参考)

    转自: https://www.cnblogs.com/ysw-go/p/11396343.html 第一部分:单机模式 1)下载地址:http://www.pirbot.com/mirrors/ap ...

  3. 大数据(bigdata)练习题

    1.在HDFS文件系统的根目录下创建递归目录“1daoyun/file”,将附件中的BigDataSkills.txt文件,上传到1daoyun/file目录中,使用相关命令查看文件系统中1daoyu ...

  4. Vue触发隐藏input file的方法

    1.使用input透明覆盖法 将input的z-index设置为1以上的数字并覆盖到需点击的内容上,将input的样式opacity设置为0(即为透明度为0),这样通过绑定在input上的change ...

  5. Android开发环境搭建(studio版)

    1.下载安装JDK 网址:http://www.oracle.com/technetwork/java/javase/downloads/index.html

  6. C++之cmath常用库函数一览

    cmath是c++语言中的库函数,其中的c表示函数是来自c标准库的函数,math为数学常用库函数. cmath中常用库函数: 函数 作用 int abs(int i); 返回整型参数i的绝对值 dou ...

  7. 幻数浅析(Magic Number)

    在源代码编写中,有这么一种情况:编码者在写源代码的时候,使用了一个数字,比如0x2123,0.021f等,他当时是明白这个数字的意思的,但是别的程序员看他的代码,可能很难理解,甚至,过了一段时间,代码 ...

  8. Mac/Windows 跳过sourcetree的注册环节

      前几天在电脑上装了个sourcetree,结果它硬要我注册,烦得很.   于是查了一下怎么跳过注册环节,结果还真有,试了一下,真给力!   特此记录. MAC版本: 打开sourcetree 关闭 ...

  9. 《MIT 6.828 Lab 1 Exercise 12》实验报告

    本实验的网站链接:MIT 6.828 Lab 1 Exercise 12. 题目 Exercise 12. Modify your stack backtrace function to displa ...

  10. IDEA操作之test case coverage的方法

    作用:  用于自动化测试,检查单元测试的覆盖率情况. 安装: 1.点击 Run * with coverage 或者右键已经定义为test source的package选择(单个test class同 ...