[SHOI2013]阶乘字符串
题意简述
-
定义一个由前 个字母组成的字符串为阶乘字符串,前 个小写字母的全排列都作为它的子序列出现。
-
给定一个字符串 ,询问它是否是阶乘字符串。
-
多组数据,, ,
分析与解答
1. 关于 的范围
-
考虑一个包含前 种小写字母的字符串,长度至少要为多少才能满足其为阶乘字符串。换句话说,构造一个由前 个字母组成的阶乘字符串,使得它的长度最小。
-
容易想到一种最 naive 的构造方式,将前 个小写字母顺序排列再复制 次,得到一个长度为 的字符串。例如,当 时,为 。容易证明该字符串一定为阶乘字符串。
-
在上面的构造方式中再改进一步,删去一些无用的字符,可以得到一种形如 的构造方式(即以 为基础串,将当前基础串接在前一个基础串的后面)(表述不太清楚,理解万岁,理解万岁),这种构造方式长度为
-
目前,我还没有找到更优秀的构造方式。也就是说,现在可以假定,一个字符串的长度至少为 ,它才可能是前 个字母的阶乘字符串。
-
注意到 ,所以当 时一定无解。
-
通过上面的分析,我们将 范围将至了 ,这就可以保证接下来的 dp 的时空复杂度了。
2. 关于如何 dp
-
如果以考虑前第 个字符为状态,每次转移时都需要枚举之前的所有子序列并一个一个 check,显然会超时。
-
注意到 ,我们不妨抛开上一个思想,考虑状压小写字母。令 表示 中的小写字母集合组成的所有全排列,是否都在 的子序列中出现过。
-
于是转移又成了很大的问题。
-
这种时候,常见的想法是改变状态。令 表示 中的小写字母集合组成的所有全排列,在 中全部出现过,所需要的下标最小是多少。此时,如果一个字母集合 不能达成阶乘字符串的要求,则 。
-
构造 的序列自动机,即 表示第 个位置后,字符 第一次出现的位置,其中 。
-
这种状态下的转移不妨使用刷表法。只需要考虑在当前集合中加入一种小写字母,会发生什么变化。
-
发现如果加入一种字母 ,则在原来子序列的最后只要加入一个 来满足有以 结尾的排列;而包含 的其他排列会在其它的转移中被考虑到。
-
所以转移就为 。因为要保证所有的排列都能够被满足,每个 在向它转移的 中取最大值。
-
最后判断 是否满足条件即可。
Code
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int MAXM = 460;
const int MAXN = 23;
const int MAXS = (1<<21) + 10;
const int INF = 0x3f3f3f3f;
int n, m, T;
char s[MAXN];
int f[MAXS], nxt[MAXM][MAXN];
inline bool work()
{
scanf("%d%s",&n,s+1);
int m = strlen(s+1);
if(n > 21) return 0;
for(int i=0;i<n;i++)
nxt[m][i] = INF;
for(int i=m;i>=1;i--) // 序列自动机 nxt
{
for(int j=0;j<n;j++)
nxt[i-1][j] = nxt[i][j];
nxt[i-1][s[i]-'a'] = i;
}
int maxs = 1<<n;
for(int i=0;i<maxs;i++)
f[i] = 0;
for(int s=0;s<maxs;s++)
{
if(f[s] == INF) return 0; // 如果子集都无法满足,全部也一定不满足
for(int i=0;i<n;i++)
{
if(((~s)>>i)&1)
{
if(f[s] == INF) f[s|(1<<i)] = INF;
else f[s|(1<<i)] = max(f[s|(1<<i)], nxt[f[s]][i]);
}
}
}
return f[maxs-1] != INF;
}
int main()
{
scanf("%d",&T);
while(T--) puts(work()?"YES":"NO");
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?