Solution of TIOJ 2070 - Special Judge。
Description
給你由小寫英文字母組成的字串$X$,請判斷是不是每一個前$K$個英文字母的子集都是$X$的子序列。
Observation 1
「前$k$個英文字母的『子集』的排列」都是子序列,其實就相當於要你判斷「是否前$k$個英文字母的的排列」都是子序列
(abc的所有排列都是,那a, b, c, ab, ac, bc, abc也當然都會是)。
Observation 2
範圍:$K \le 20$ $\rightarrow$位元DP!
Solution
定義dp[S] = 當狀態為S時,字串所需的最短長度能夠使得滿足題目的要求,當dp[S]為n+1時,表示狀態S無法滿足。
另外定義pos[i][j]表示在位置j往右出現字元i最近的位置,-1表示右邊找不到i。
轉移式:$dp[i + (1 << j)] = max(dp[i + (1 << j)], pos[j][dp[i]]);$
轉移過程中只要有出現了任何一個pos = -1就輸出$No$,否則就輸出$Yes$。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| #include <bits/stdc++.h> #define int long long #define pb push_back #define all(x) (x).begin(), (x).end() #define fastio cin.tie(0); ios_base::sync_with_stdio(false); #define INF 1e15+9 using namespace std; const int maxn = 2e5+5; const int MOD = 1e9+7; typedef pair<int,int> P;
void solve(){ int k; string s; cin >> k >> s; char myChar[1005] = {}; for(int i=1;i<=s.size();i++) myChar[i] = s[i-1]; int pos[50][1001]; for(int i=0;i<k;i++){ int curPos = -1; for(int j=s.size();j>=0;j--){ pos[i][j] = curPos; if(myChar[j] - 'a' == i) curPos = j; } } int S = (1 << k); int dp[S+5] = {}; for(int i=0;i<S;i++){ for(int j=0;j<k;j++){ if((i >> j) % 2 == 0){ int nearest = pos[j][dp[i]]; if(nearest == -1){ cout << "No" << '\n'; return; } dp[i + (1 << j)] = max(dp[i + (1 << j)], pos[j][dp[i]]); } } } cout << "Yes" << '\n'; }
signed main(){fastio int T = 1; cin >> T; while(T--){ solve(); } }
|
Bonus
這一題的前身是「構造一個最短字串滿足此性質」,此題是一個co-NP-complete的問題
目前能找到的最短且有系統的構造方法如下:
1 2 3 4 5 6 7 8
| for(int i=1;i<=k * k - 2 * k + 4;i++){ if(i <= k) cout << (char)('a' + i - 1); else if(i > k && (i - 2) % (k - 1) == 0) cout << 'a'; else cout << (char)((int)floor((i - 1) * (k - 2) / (k - 1)) % (k - 1) + 2 + 'a' - 1); }
|
長度:$k^2 - 2k + 4$