P1368 【模板】最小表示法

最近想起来了这个问题,记得当时做《算法竞速进阶指南》见过。

不过现在忘记的差不多了,打算重新推一次。

假设当前最优的是1开头,然后我们用从2到n枚举以i开头的表示和当前最优的哪个更优。

假设现在匹配了j个字符,第j+1个不同,分两种情况讨论:

  1. 当前更优,那么更新
  2. 原来更优,i应该增加k+1,因为后面的那一部分不如前面的优。也就是 ∀ t ∈ [ 0 , k ] , S i + t > S a n s + t \forall t\in[0,k],S_{i+t}>S_{ans+t} t[0,k],Si+t>Sans+t

特别地,如果n个字符相等,那么就出现了循环节,直接退出即可。

这样写了个代码

#include <bits/stdc++.h>
using namespace std;

const int N = 6e5 + 10;

int n, a[N];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        a[n + i] = a[i];
    }
    int ans = 1;
    for (int i = 2; i <= n; i++) {
        int j = 0;
        while (j < n && a[ans + j] == a[i + j]) {
            j++;
        }
        if (j == n) {
            break;
        }
        if (a[ans + j] < a[i + j]) {
            i += j;
        }
        else {
            ans = i;
        }
    }
    for (int i = 1; i <= n; i++) {
        cout << a[ans + i - 1] << " \n"[i == n];
    }
}

后面仔细分析了一下发现复杂度不对,主要是更新那个地方.可以构造数据。

#include <bits/stdc++.h>
using namespace std;

int main() {
    freopen("in.in", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n = 299998;
    cout << n << endl;
    for (int i = 1; i <= 99999; i++) {
        cout << "1 2 3 ";
    }
    cout << "0\n";
}

卡掉。

后面想着ans和i应该也有相同的性质,如果比不过就应该直接跳,这样就想到了双指针的做法,基本和书上一样了。

#include <bits/stdc++.h>
using namespace std;

const int N = 6e5 + 10;

int n, a[N];

int main() {
    // freopen("in.in", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        a[n + i] = a[i];
    }
    int i = 1, j = 2, k;
    while (i <= n && j <= n) {
        for (k = 0; k < n && a[i + k] == a[j + k]; k++);
        if (k == n) {
            break;
        }
        if (a[i + k] < a[j + k]) {
            j += k + 1;
        }
        else {
            i += k + 1;
        }
        if (i == j) {
            i++;
        }
    }
    int pos = min(i, j);
    for (int i = 1; i <= n; i++) {
        cout << a[pos + i - 1] << " \n"[i == n];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值