Description&Data Constraint
给定一个由前n个小写字母组成的串S。
串S是阶乘字符串当且仅当前n个小写字母的全排列(共n!种)都作为S的子序列(可以不连续)出现。
由这个定义出发,可以得到一个简单的枚举法去验证,但是它实在太慢了。所以现在请你设计一个算法,在1秒内判断出给定的串是否是阶乘字符串。

Solution
首先根据数据范围可以发现当n>21n>21n>21的时候是没有解的,详情请见:

预处理nxti,jnxt_{i,j}nxti,j表示sss中iii之后的第一个jjj的位置
设fif_ifi表示iii中的字母在sss中存在全排列的最小下标,那么每次加入新的字母jjj,可以得出转移方程
fi∣(1<<j)=max(nxtfi,j)f_{i|(1<<j)}=\max(nxt_{f_i,j})fi∣(1<<j)=max(nxtfi,j)
取最大值是为了使得全排列都有的选择
Code
#include<cstdio>
#include<cstring>
#include<iostream>
#define L 455
#define N 30
#define inf 0X3f3f3f3f
using namespace std;
int t,n,m,a[N],f[4000000],nxt[L][N];
char s[L];
int main()
{
scanf("%d",&t);
while (t--)
{
scanf("%d%s",&n,s+1);
m=strlen(s+1);
if (n>21)
{
printf("NO\n");
continue;
}
for (int i=0;i<n;++i)
nxt[m][i]=inf;
for (int i=m;i;--i)
{
for (int j=0;j<n;++j)
nxt[i-1][j]=nxt[i][j];
nxt[i-1][s[i]-'a']=i;
}
memset(f,0,sizeof(f));
for (int s=0;s<(1<<n);++s)
{
if (f[s]==inf)
{
f[(1<<n)-1]=inf;
break;
}
for (int i=0;i<n;++i)
{
if ((s|(1<<i))>s)
{
if (f[s]==inf) f[s|(1<<i)]=inf;
else f[s|(1<<i)]=max(f[s|1<<i],nxt[f[s]][i]);
}
}
}
if (f[(1<<n)-1]!=inf) printf("YES\n");
else printf("NO\n");
}
return 0;
}

本文介绍了一种更快的方法来判断给定小写字母串是否为阶乘字符串,通过预处理和转移方程,能在1秒内解决n>21的情况。代码展示了如何利用nxt数组和f数组进行字符串全排列子序列查找。
240

被折叠的 条评论
为什么被折叠?



