[状压 DP] BZOJ 4416 [Shoi2013]阶乘字符串

传送门:http://blog.csdn.net/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;
}


你可能感兴趣的:([状压 DP] BZOJ 4416 [Shoi2013]阶乘字符串)