【SHTSC2013】階乘字串 題解
阿新 • • 發佈:2021-07-05
神奇結論
時,可以滿足使這若干個字母成為階乘子序列的需要在目標串中選取的最少的前幾個字母
為了方便轉移,我們要構建一個基礎的子序列自動解\(next[i][j]\)表示在目標串第\(i\)的位置以後第一次出現字母\(j\)的位置
然後我們考慮用\(i\)這個狀態往其他狀態轉移,轉移方程是:
\(f[i|(1<<j-1)]=max(f[i|(1<<j-1)],next[f[i]][j])\) (\(j\)表示列舉的字母)
這是考慮將字母\(j\)加在最後面的,而字母\(j\)加在中間的情況在轉移別的狀態是已經或將會被考慮到
然後為什麼是取\(max\)呢?因為要保證所有的排列均可滿足
最後判斷\(f[(1<<i-1)-1]\) 合不合法即可
【SHTSC2013】階乘字串
Description
給定一個由前n個小寫字母組成的串S。
串S是階乘字串當且僅當前n個小寫字母的全排列(共n!種)都作為S的子序列(可以不連續)出現。
由這個定義出發,可以得到一個簡單的列舉法去驗證,但是它實在太慢了。所以現在請你設計一個演算法,在1秒內判斷出給定的串是否是階乘字串。
Input
輸入第1行一個整數T,表示這個檔案中會有T組資料。
接下來分T個塊,每塊2行:
第1行一個正整數n,表示S由前n個小寫字母組成。
第2行一個字串S。
Output
對於每組資料,分別輸出一行。每行是YES或者NO,表示該資料對應的串S是否是階乘字串。
Sample Input
2
2
bbaa
2
aba
Sample Output
NO
YES
Data Constraint
Hint
樣例解釋:
第一組資料中,ab這個串沒有作為子序列出現。
題解
這題目乍一看不好下手,但是我們知道只有26個字母,所以可以考慮狀壓DP
但是,如果按照常規的思路來說,這道題的複雜度是\(O(2^n*n)\)的,當\(n=26\)時,複雜度大概在17億左右,是過不了的
But這題有個神奇的結論:就是當\(n>21\)是必定無解,證明的思路詳見此,雖然這個證法也是有點假的,但是它給我們提供了一個很好的思路
重新回到這題的題解,我們設\(f[i]\)表示當前選取的字母集合為二進位制狀態\(i\)
為了方便轉移,我們要構建一個基礎的子序列自動解\(next[i][j]\)表示在目標串第\(i\)的位置以後第一次出現字母\(j\)的位置
然後我們考慮用\(i\)這個狀態往其他狀態轉移,轉移方程是:
\(f[i|(1<<j-1)]=max(f[i|(1<<j-1)],next[f[i]][j])\) (\(j\)表示列舉的字母)
這是考慮將字母\(j\)加在最後面的,而字母\(j\)加在中間的情況在轉移別的狀態是已經或將會被考慮到
然後為什麼是取\(max\)呢?因為要保證所有的排列均可滿足
最後判斷\(f[(1<<i-1)-1]\)
CODE
#include<cstdio>
#include<string>
#include<cstring>
#define max(a,b) (((a)>(b))?(a):(b))
#define min(a,b) (((a)<(b))?(a):(b))
#define R register int
#define N 505
#define ll long long
#define M 4000005
#define K 30
#define inf 0x3f3f3f3f
using namespace std;
int t,n,len,f[M],next[N][K];
char s[N];
inline void read(int &x)
{
x=0;int f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();x*=f;
}
inline bool pd()
{
if (n>21) return 0;
memset(f,0,sizeof(f));
for (R i=1;i<=n;++i)
next[len][i]=inf;
for (R i=len;i;--i)
{
for (R j=1;j<=n;++j)
next[i-1][j]=next[i][j];
next[i-1][s[i]-96]=i;
}
int tot=1<<n;
for (R i=0;i<tot;++i)
{
if (f[i]==inf) return 0;
//如果當前狀態無法達到,那麼以它來轉移的所有狀態也是無法達到的
for (R j=1;j<=n;++j)
if (!(i&(1<<j-1)))
f[i|(1<<j-1)]=max(f[i|(1<<j-1)],next[f[i]][j]);
}
return f[tot-1]!=inf;
}
int main()
{
read(t);
while (t--)
{
read(n);scanf("%s",s+1);len=strlen(s+1);
if (pd()) printf("YES\n");else printf("NO\n");
}
return 0;
}