BZOJ2882 && Gym - 101875B 最小表示法

循环字符串的最小表示法的问题可以这样描述:对于一个字符串S,求S的循环的同构字符串S’中字典序最小的一个。
设S=bcad,且S’是S的循环同构的串。S’可以是bcad或者cadb,adbc,dbca。而且最小表示的S’是adbc

那么解决这个问题我们可以使用O(n)的最小表示法算法,用来求循环移位的最优解

分为以下步骤:

1.初始化,让i=0,j=1,k=0,其中i,j,k表示的是以i开头和以j开头的字符串的前k个字符相同
2.分为三种情况:
(1)如果str[i+k]==str[j+k] k++。
(2)如果str[i+k] > str[j+k] i = i + k + 1,即最小表示不可能以str[i]开头。
(3)如果str[i+k] < str[j+k] j = j + k + 1,即最小表示不可能以str[j]开头。
那么只要循环n次,就能够判断出字符串的最小表示是以哪个字符开头。
下面是板子:

int solve(int *s, int n) { //最小表示法,返回那个位置,
    int i = 0, j = 1, k = 0;
    while (i < n && j < n && k < n) {
        int t = s[(i + k) % n] - s[(j + k) % n];
        if (t == 0) k++;
        else if (t < 0) {
            j = j + k + 1;
            k = 0;
        } else if (t > 0) {
            i = i + k + 1;
            k = 0;
        }
        if (i == j) j++;
        int flag = 1;
    }
    return min(i, j);//这是会出现i>j的情况
}
//BZOJ2882
//裸题,套板子就行
#include <bits/stdc++.h>
#define eps 1e-8
#define INF 0x3f3f3f3f
#define PI acos(-1)
#define lson l,mid,rt<<1
#define rson mid+1,r,(rt<<1)+1
#define CLR(x,y) memset((x),y,sizeof(x))
#define fuck(x) cerr << #x << "=" << x << endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int seed = 131;
const int maxn = 5e5 + 5;
const int mod = 1e9 + 7;
int n;
int a[maxn];
int solve(int *s, int n) { //最小表示法,返回那个位置,
    int i = 0, j = 1, k = 0;
    while (i < n && j < n && k < n) {
        int t = s[(i + k) % n] - s[(j + k) % n];
        if (t == 0) k++;
        else if (t < 0) {
            j = j + k + 1;
            k = 0;
        } else if (t > 0) {
            i = i + k + 1;
            k = 0;
        }
        if (i == j) j++;
        int flag=1;
        if(i>j+3) flag=0;
        assert(flag);
    }
    return min(i, j);//
}
int main() {
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }
    int x = solve(a, n);

    for (int i = 0; i < n; i++) {
        printf("%d ", a[(i + x) % n]);
    }
    return 0;
}



题目要求让所有经过循环移位的数字都要比原数字小,那么也就是只有当这个数字是该数字的最小表示法的时候,才能满足题意。

//Gym - 101875B 
#include <bits/stdc++.h>
#define eps 1e-8
#define INF 0x3f3f3f3f
#define PI acos(-1)
#define lson l,mid,rt<<1
#define rson mid+1,r,(rt<<1)+1
#define CLR(x,y) memset((x),y,sizeof(x))
#define fuck(x) cerr << #x << "=" << x << endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int seed = 131;
const int maxn = 5e5 + 5;
const int mod = 1e9 + 7;
int n;
int a[maxn], b[maxn];
int solve(int *s, int n) { //最小表示法,返回那个位置,
    int i = 0, j = 1, k = 0;
    while (i < n && j < n && k < n) {
        int t = s[(i + k) % n] - s[(j + k) % n];
        if (t == 0) k++;
        else if (t < 0) {
            j = j + k + 1;
            k = 0;
        } else if (t > 0) {
            i = i + k + 1;
            k = 0;
        }
        if (i == j) j++;
    }
    return min(i, j);//这是会出现i > j的情况
}
int main() {
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%1d", &a[i]);
    }
    int x = solve(a, n);//求出最小表示法的初始位置
    for (int i = 0; i < n; i++) {
        b[i] = a[(i + x) % n];
    }
    for (int i = 0; i < n; i++) {
        if (b[i] != a[i]) {
            printf("No\n");
            return  0;
        }
    }
    printf("Yes\n");

    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值