JZOJ 3293. 【SHTSC2013】階乘字串
阿新 • • 發佈:2021-07-05
\(\text{Problem}\)
給定一個由前 \(n\) 個小寫字母組成的串 \(S\)。
串 \(S\) 是階乘字串當且僅當前 \(n\) 個小寫字母的全排列(共 \(n!\) 種)都作為 \(S\) 的子序列(可以不連續)出現。
判斷 \(S\) 是否是階乘字串
多組資料
\(\text{Analysis}\)
一個結論:
當 \(n > 21\) 時
\(\because |S| <= 450\)
\(\therefore C_{n}^{450} < n!\)
\(\therefore\) 結果為 \(NO\)
於是我們只需考慮 \(n \le 21\) 的情況
此時狀壓可行
設 \(f_s\)
那麼我們只需要看 \(f_{2^n-1}\) 是否合法
設 \(nxt_{i,j}\) 表示串 \(S\) 中位置 \(i\) 以後(不包括 \(i\)) \(j\) 出現的位置
則 \(f_s = \max_{i \in s} nxt[f_{s - 2 ^ i}][i]\)
\(\text{Code}\)
#include<cstdio> #include<cstring> #include<iostream> using namespace std; const int MAXN = 21; int T, n, len, nxt[455][26], f[1 << MAXN]; char s[455]; inline int solve() { memset(nxt, 0x3f3f3f3f, sizeof nxt), memset(f, 0, sizeof f); len = strlen(s + 1); for(int j = 0; j <= len + 1; j++) for(int k = 0; k < 26; k++) nxt[j][k] = len + 1; for(int j = len; j >= 0; j--) { for(int k = 0; k < 26; k++) nxt[j][k] = nxt[j + 1][k]; nxt[j][s[j + 1] - 'a'] = j + 1; } for(int i = 1; i < (1 << n); i++) { for(int j = 0; j < n; j++) if (i & (1 << j)) f[i] = max(f[i], nxt[f[i - (1 << j)]][j]); } return f[(1 << n) - 1] != len + 1; } int main() { scanf("%d", &T); for(int i = 1; i <= T; i++) { scanf("%d%s", &n, s + 1); if (n > 21){printf("NO\n"); continue;} if (solve()) printf("YES\n"); else printf("NO\n"); } }