[BZOJ4416][SHOI2013]階乘字串(子集DP)
阿新 • • 發佈:2018-12-02
怎麼也沒想到是子集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 7int 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 }