传送门:http://blog.youkuaiyun.com/lych_cys/article/details/50822551
令g[i][c]表示i之后第一个为c的字母的下标。
令i为用二进制表示的已经选取的字母的集合,令f[i]表示选取的字母的集合的全排列都出现的最短的原串的前缀的长度。然后枚举字母转移。显然当且仅当f[2^n-1]<=字符串长度时有解。
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define lowbit(x) ((x)&-(x))
using namespace std;
inline char nc()
{
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}
inline void read(int &x)
{
char c=nc(),b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
inline int read(char *x)
{
char c=nc(); int len=0;
for (;!(c>='a' && c<='z');c=nc());
for (;c>='a' && c<='z';x[++len]=c,c=nc()); x[++len]=0; return len-1;
}
int n,len;
char s[455];
int g[455][36],head[36];
int bin[3000005],f[3000005];
int main()
{
for (int i=1,j=2;i<=21;i++,j<<=1)
bin[j]=i;
int Q;
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
read(Q);
while (Q--)
{
read(n);
len=read(s);
if (n>21) { printf("NO\n"); continue; }
for (int i=0;i<26;i++) head[i]=len+1;
memset(g,0,sizeof(g)); memset(f,0,sizeof(f));
for (int i=len+1;i>=0;i--)
{
for (int j=0;j<n;j++)
g[i][j]=head[j];
if (i>=1 && i<=len) head[s[i]-'a']=i;
}
for (int i=1;i<(1<<n);i++)
{
for (int j=i;j;j-=lowbit(j))
{
int pos=bin[lowbit(j)];
f[i]=max(f[i],g[f[i-lowbit(j)]][pos]);
}
}
if (f[(1<<n)-1]>len)
printf("NO\n");
else
printf("YES\n");
}
return 0;
}