CF1537E2 Erase and Extend (Hard Version) 題解
阿新 • • 發佈:2021-06-19
一、題目:
二、思路:
這題不知道為什麼官方題解寫得那麼複雜。還需要用到擴充套件KMP,即Z函式。感謝Robert_JYH巨佬提供的思路,讓我明白了一種非常簡單的做法。
首先,我們可以證明,最優解一定是通過先執行操作1,後執行操作2來得到的(當然也可以不進行操作1,直接進行操作2)。因為如果最優解是執行了一些操作2之後,再執行的操作1,那麼我們發現把操作1放到操作2之前操作一定會使答案變得不劣。
所以問題就轉化成給定一個字串 \(s\),需要保留 \(s\) 的一個字首 \(pre\),同時讓 \(pre\) 複製若干次,得到一個長度為 \(k\) 的字串(多餘的部分刪去)。目標是讓最終的字串的字典序最小。
暴力當然很簡單了,我們列舉所有字首 \(pre\),模擬這個過程,更新答案即可。你就可以成功地解決這道題的簡單版了。
那麼現在來考慮線性的做法。
我們設當前最優的字首為 \(s[0\sim p-1]\),現在列舉到的字首是 \(s[0\sim i]\)。那麼分為三種情況。(字串的下標從0開始。)
- 若 \(s[i]>s[i\bmod p]\),那麼非常遺憾,\(i\) 及 \(i\) 以後的所有字首均不能作為最優答案。直接跳出迴圈。
- 若 \(s[i]<s[i\bmod p]\),我們發現用字首 \(s[0\sim i]\) 比字首 \(s[0\sim p-1]\) 更優,於是令 \(p=i+1\)
- 若 \(s[i]=s[i\bmod p]\),這並不能說明什麼,我們只能什麼都不做。
然後呢?然後你就會驚奇的發現,你寫完了一道 Div2 難度的第六題!
三、程式碼:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; #define FILEIN(s) freopen(s".in", "r", stdin); #define FILEOUT(s) freopen(s".out", "w", stdout) #define mem(s, v) memset(s, v, sizeof s) inline int read(void) { int x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return f * x; } int n, K; string s; int main() { n = read(); K = read(); cin >> s; int p = 1, i = 0, n = s.length(); for (; i < n; ++ i) { if (s[i] > s[i % p]) break; if (s[i] < s[i % p]) p = i + 1; } for (i = 0; i < K; ++ i) putchar(s[i % p]); puts(""); return 0; }