BZOJ4416 SHOI2013阶乘字符串(状压dp)
当n大到一定程度(>21)时一定无解,并不会证。
如果要取出一个排列,显然应该让每一位在序列中的位置尽量靠前。于是设f[S]表示存在S子集中这些字母所组成的所有排列的最短前缀的长度,枚举当前排列最后一个字母转移即可。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 510 #define M 21 int T,n,m,a[N],f[1<<M],nxt[N][M]; char s[N]; int main() { #ifndef ONLINE_JUDGE freopen("bzoj4416.in","r",stdin); freopen("bzoj4416.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif T=read(); while (T--) { m=read();scanf("%s",s+1);n=strlen(s+1); if (m>21) {cout<<"NO"<<endl;continue;} for (int i=1;i<=n;i++) a[i]=s[i]-'a'; for (int i=0;i<m;i++) nxt[n][i]=nxt[n+1][i]=n+1; for (int i=n-1;i>=0;i--) for (int j=0;j<m;j++) nxt[i][j]=a[i+1]==j?i+1:nxt[i+1][j]; memset(f,0,sizeof(f)); int S=(1<<m)-1; for (int i=1;i<=S;i++) for (int j=0;j<m;j++) if (i&(1<<j)) f[i]=max(f[i],nxt[f[i^(1<<j)]][j]); if (f[S]<=n) cout<<"YES"<<endl; else cout<<"NO"<<endl; } return 0; }