https://vjudge.net/contest/369905#problem/J
题意:给一个n和一个字符串s,要求字母表前n个字符的n!个全排列,s都有子序列与它对应
解题思路:
dp[i][j]记录字符串中第i个位置之后最早出现的第j字母,注意初始化dp[len][…]=dp[len+1][…]=len+1; i位置后没有第j个字母的设置为len+1
for(int i=0;i<n;i++)
dp[len][i]=len+1,dp[len+1][i]=len+1;
for(int i=len;i>=1;i--)
{
for(int j=0;j<n;j++)
if(j==s[i]-'a')
dp[i-1][j]=i;
else
dp[i-1][j]=dp[i][j];
}
f[i]表示状态i全排列出现的最早位置,即比如状态为0101,表示的是ac和ca全部出现的最早位置
int length=(1<<n)-1;
for(int i=1;i<=length;i++)
{
for(int j=0;j<n;j++)
if(i&(1<<j))
f[i]=max(f[i],dp[f[i^(1<<j)]][j]);
//i^(1<<j)表示第j个字母为最后一位的情况
// f[i^(1<<j)]表示其出现的最早位置
//dp[f[i^(1<<j)]][j]表示这个状态之后j最早出现的位置,也就是要求的f[i]的值
}
另外当n>21时,直接判断情况不成立(怎么证明我也不会)
#include<cstdio>
#include<iostream>
#include<string.h>
using namespace std;
int t;
int n;
int len;
int f[1<<22];
int dp[500][30];
char s[500];
int main()
{
scanf("%d",&t);
while(t--)
{
memset(f,0,sizeof(f));
memset(dp,0,sizeof(dp));
scanf("%d",&n);
scanf("%s",s+1);
if(n>21){
printf("NO\n");
continue;
}
len=strlen(s+1);
for(int i=0;i<n;i++)
dp[len][i]=len+1,dp[len+1][i]=len+1;
for(int i=len;i>=1;i--)
{
for(int j=0;j<n;j++)
if(j==s[i]-'a')
dp[i-1][j]=i;
else
dp[i-1][j]=dp[i][j];
}
int length=(1<<n)-1;
for(int i=1;i<=length;i++)
{
for(int j=0;j<n;j++)
if(i&(1<<j))
f[i]=max(f[i],dp[f[i^(1<<j)]][j]);
}
if(f[length]==len+1)
printf("NO\n");
else
printf("YES\n");
}
return 0;
}