BZOJ4416: [Shoi2013]阶乘字符串

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

Sample Output

NO
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;
}
View Code

 

posted @ 2016-03-09 19:00  wzj_is_a_juruo  阅读(420)  评论(3编辑  收藏