1. 程式人生 > 其它 >CF1537E2 Erase and Extend (Hard Version) 題解

CF1537E2 Erase and Extend (Hard Version) 題解

一、題目:

codeforces原題

洛谷原題

二、思路:

這題不知道為什麼官方題解寫得那麼複雜。還需要用到擴充套件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開始。)

  1. \(s[i]>s[i\bmod p]\),那麼非常遺憾,\(i\)\(i\) 以後的所有字首均不能作為最優答案。直接跳出迴圈。
  2. \(s[i]<s[i\bmod p]\),我們發現用字首 \(s[0\sim i]\) 比字首 \(s[0\sim p-1]\) 更優,於是令 \(p=i+1\)
  3. \(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;
}