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

怎么也没想到是子集DP,想到了应该就没什么难度了。

首先n>21时必定为NO。

g[i][j]表示位置i后的第一个字母j在哪个位置,n*21求出。

f[S]表示S的所有全排列子序列出现的最后末尾位置,枚举最后一个字母转移。21*2^21

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 5 using namespace std;
 6 
 7 int T,n,m,k,t,g[500][26],f[1<<21];
 8 char a[500];
 9 
10 int main(){
11     freopen("bzoj4416.in","r",stdin);
12     freopen("bzoj4416.out","w",stdout);
13     scanf("%d",&T);
14     while(T--){
15         scanf("%d%s",&n,a+1); m=strlen(a+1);
16         if (n>21){ puts("NO"); continue; }
17         rep(j,0,n-1) g[m][j]=g[m+1][j]=m+1;
18         for(int i=m; i; i--){
19             rep(j,0,n-1) g[i-1][j]=g[i][j];
20             g[i-1][a[i]-'a']=i;
21         }
22         rep(i,1,(1<<n)-1){
23             int res=0;
24             for(int j=i; j; j-=j&-j)
25                 k=__builtin_ctz(j),res=max(res,g[f[i^(1<<k)]][k]);//ctz统计末尾0的个数
26             f[i]=res;
27         }
28         puts(f[(1<<n)-1]>m ? "NO" : "YES");
29     }
30     return 0;
31 }
原文地址:https://www.cnblogs.com/HocRiser/p/10053072.html