Description
给定一个由前n个小写字母组成的串S。
串S是阶乘字符串当且仅当前n个小写字母的全排列(共n!种)都作为S的子序列(可以不连续)出现。
由这个定义出发,可以得到一个简单的枚举法去验证,但是它实在太慢了。所以现在请你设计一个算法,在1秒内判断出给定的串是否是阶乘字符串。
Input
输入第1行一个整数T,表示这个文件中会有T组数据。
接下来分T个块,每块2行:
第1行一个正整数n,表示S由前n个小写字母组成。
第2行一个字符串S。
Output
对于每组数据,分别输出一行。每行是YES或者NO,表示该数据对应的串S是否是阶乘字符串。
Sample Input
2
2
bbaa
2
aba
2
bbaa
2
aba
Sample Output
NO
YES
【样例解释】
第一组数据中,ab这个串没有作为子序列出现。
N<=26
T<=5
|S|<=450
YES
【样例解释】
第一组数据中,ab这个串没有作为子序列出现。
N<=26
T<=5
|S|<=450
设f[S]表示当S中集合中的字母构成的排列均在原序列[1,f[S]]出现的最小值。
枚举排列的最后一个元素,则f[S]=max(next[f[S^(1<<i)]][i]),其中next[i][j]表示从i开始下一个j出现的位置。
然后我就不明白怎么舍解了,上网搜了一下发现n<=21但我并不知道为什么这样可以2333(如有能证明的神犇请留言)。
#include<cstdio> #include<cctype> #include<queue> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int maxn=510; const int maxm=1<<22; int n,m,f[maxm],g[maxn][23]; char s[maxn]; void solve() { n=read();scanf("%s",s+1);m=strlen(s+1); if(n>21) {puts("NO");return;} dwn(i,m+1,0) { rep(j,0,n-1) g[i][j]=(i>=m?m+1:g[i+1][j]); if(i!=m) g[i][s[i+1]-'a']=i; } rep(S,1,(1<<n)-1) { int c=0; rep(i,0,n-1) if(S>>i&1) c=max(c,g[f[S^(1<<i)]][i]); f[S]=c; } puts(f[(1<<n)-1]<=m?"YES":"NO"); } int main() { dwn(T,read(),1) solve(); return 0; }