CF1550E Stringforces
CF1550E Stringforces
題意:
設 s 是一個由前 k 個小寫字母構成的字符串,v 是前 k 個小寫字母中的某一個。定義MaxLen(s,v)\mathrm{MaxLen}(s,v)MaxLen(s,v) 表示 s 所有僅由字母 v 構成的連續子串的最長長度。
定義 s 的價值為所有 MaxLen(s,v)\mathrm{MaxLen}(s,v)MaxLen(s,v) 的最小值,其中 v 取遍前 k 個小寫字母。
現在給定一個長度為 n 的字符串 s,s 中字母要么是前 k 個小寫字母中的某一個,要么是問號。你需要將 s 中的每一個問號替換成前 k 個小寫字母中的一個,并最大化 s 的價值。方便起見,你只需要輸出這個最大的價值即可。
保證 1≤n≤2×105,1≤k≤171\leq n\leq 2\times 10^5 ,1\leq k\leq 171≤n≤2×105,1≤k≤17。
題解:
又是一個狀壓dp與線性dp的結合
k<=17,求最大化的MaxLen(s,v)的最小值
不難想到是狀壓dp+二分,然后就卡住了
二分答案,然后判斷mid是否成立,mid成立的條件是:要滿足所有的MaxLen(s,v)\mathrm{MaxLen}(s,v)MaxLen(s,v)大于等于mid。那我們就在原串中找是否存在一個長度為mid的全部由第i個字符構成的連續子串(i∈k),且這k個區間互不相交
然后就可以再check中利用狀壓dp判斷mid,設dp[i]表示已經解決需求的字符種類的狀態為x時,所選區間的最右端點的最小值。很顯然當dp[i]<=n是合法的,說明可以取到這樣的區間
對于dp的轉移,我們需要知道指定字符在之后區間出現的位置,這樣才知道是否可以轉移,所有我們設數組pos[i][j]pos[i][j]pos[i][j]表示區間[j,n]匹配字符i的最早位置,預處理出pos,這樣之后就可以O(1)轉移
狀態轉移方程為:
dp[x]=min(1<<i)∈xpos[i][dp[x?(1<<i)]+1]dp[x]=min_{(1<<i)∈x}pos[i][dp[x-(1<<i)]+1]dp[x]=min(1<<i)∈x?pos[i][dp[x?(1<<i)]+1]
含義就是:狀態x可以由狀態x-(1<<i),然后在區間[dp[x?(1<<i)]+1,n][dp[x-(1<<i)]+1,n][dp[x?(1<<i)]+1,n]中選擇字符i轉移得到
代碼:
#include <bits/stdc++.h> #include <unordered_map> #define debug(a, b) printf("%s = %d\n", a, b); using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> PII; clock_t startTime, endTime; //Fe~Jozky const ll INF_ll= 1e18; const int INF_int= 0x3f3f3f3f; void read(){}; template <typename _Tp, typename... _Tps> void read(_Tp& x, _Tps&... Ar) {x= 0;char c= getchar();bool flag= 0;while (c < '0' || c > '9')flag|= (c == '-'), c= getchar();while (c >= '0' && c <= '9')x= (x << 3) + (x << 1) + (c ^ 48), c= getchar();if (flag)x= -x;read(Ar...); } template <typename T> inline void write(T x) {if (x < 0) {x= ~(x - 1);putchar('-');}if (x > 9)write(x / 10);putchar(x % 10 + '0'); } void rd_test() { #ifdef ONLINE_JUDGE #elsestartTime= clock();freopen("data.in", "r", stdin); #endif } void Time_test() { #ifdef ONLINE_JUDGE #elseendTime= clock();printf("\nRun Time:%lfs\n", (double)(endTime - startTime) / CLOCKS_PER_SEC); #endif } const int maxn= 2e5 + 9; int n, k; const int mx= (1 << 18) + 9; int dp[mx]; char s[maxn]; int pos[20][mx]; bool check(int mid) {//先預處理出pos[][]for (int i= 0; i < k; i++) {int num= 0;for (int j= n; j >= 1; j--) {if (s[j] == 'a' + i || s[j] == '?') //如果是第k個字符或者是?num++; //符合我們要求,連續elsenum= 0; //否則中斷if (num >= mid) //如果連續長度大于等于midpos[i][j]= j + mid - 1; //記錄最左側情況else //如果小于midpos[i][j]= pos[i][j + 1];}}memset(dp, INF_int, sizeof(dp));dp[0]= 0;for (int i= 1; i < (1 << k); i++) { //枚舉狀態for (int j= 0; j < k; j++) { //對于每個字符考慮if ((i >> j) & 1) { //如果當前狀態有這個字符if (dp[i - (1 << j)] != INF_int) { //如果去掉這個字符的狀態存在if (pos[j][dp[i - (1 << j)] + 1]) { //如果后面的區間內有字符jdp[i]= min(dp[i], pos[j][dp[i - (1 << j)] + 1]); //最小化所有區間右端點}}}}}return dp[(1 << k) - 1] <= n; } int main() {//rd_test();read(n, k);cin >> (s + 1);int l= 1, r= n / k;int res= 0;while (l <= r) {int mid= l + r >> 1;if (check(mid)) {res= mid;l= mid + 1;}elser= mid - 1;}cout << res << endl;//Time_test(); }總結
以上是生活随笔為你收集整理的CF1550E Stringforces的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C字形视力表怎么看
- 下一篇: sdut算法分析oj题目整合