HDU5442 最小(大)表示法

本文介绍了一种高效求解字符串循环同构问题的方法——最小(大)表示法,并通过实例详细解析了如何应用于求解字符环中字典序最大或最小的问题。提供了完整的算法思路及代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最小(大)表示法

​ 一个字符串扩增到两倍的时候,在0~n-1的范围中都可以通过循环找到与原字符串同构的字符串,但是其中有两个特殊的字符串,字典序最大和字典序最小(数字也可以当做字典序来处理),直接暴力找到最小或者最大的算法复杂度是O(n^2) ,有一种最小(大)表示法可以解决此问题。

百度论文:https://wenku.baidu.com/view/c6c5e7335a8102d276a22fa6.html

博客解析:http://blog.youkuaiyun.com/zy691357966/article/details/39854359

我的理解是:扩增之后的字符串,初始指针位置不同,但是通过有条件的移动(最大或者最小)都可以找到最优的位置。

  • 需要注意的点是:有可能出现多个相同的点使得最大(最小)有多个位置,但是其找到的第一个便是最优的字典序。

HDU5442

题意:

​ 给定一个字符构成的环,在这个环中选定一个起点,顺时针或逆时针使得以选定点为起点的字符串字典序最大。如果有多个答案,优先选择使起点位置在原字符串中编号较小的,如果还有多个答案,优先选择顺时针。

思路:

​ 对于正序,直接用最大表示法来记录答案,对于逆序先逆序存储,然后找到位置最大的最优字典序(也就是对应初始位置最小)。然后比较输出。

  • 这里有一个疑惑点,对于逆序找位置最大的时候需要一直找,但是第一个找到的就能直接输出最终的位置,这是让我不理解的地方。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <math.h>
#include <stdlib.h>

using namespace std;

const int maxn = 2e4+10;

char s[maxn*2];
char s1[maxn*2];
char ans1[maxn];
char ans2[maxn];
int n;

int getMaxStringPos(char *x,int l)
{
    int i,j,k;
    i = 0,j = 1,k = 0;
    while(i < l && j < l) {
        k = 0;
        while(x[i+k] == x[j+k] && k < l) k++;
        if(k == l) return min(i,j);
        if(x[i+k] > x[j+k])
            if(j+k+1 > i) j += k+1;
            else j = i+1;
        else
            if(i+k+1 > j) i += k+1;
            else i = j+1;
    }
    return min(i,j);
}

int getMaxnixuPos(char *x,int l)
{
    int i,j,k;
    i = 0,j = 1,k = 0;
    while(i < l && j < l) {
        k = 0;
        while(x[i+k] == x[j+k] && k < l) k++;
        if(k == l) return l-abs(i-j)+i; //这一种利用到了循环节,不是很明白,时间:15ms
        /*if(k == n) {
            i = max(i,j);
            j = i + 1;
            continue;   //时间:500+ms
        }*/
        if(x[i+k] > x[j+k])
            if(j+k+1 > i) j += k+1;
            else j = i+1;
        else
            if(i+k+1 > j) i += k+1;
            else i = j+1;
    }
    if(j >= l) return i;
    return j;
}

int main()
{
//    freopen("in.txt","r",stdin);
    int t;
    scanf("%d",&t);
    while(t--) {
        scanf("%d",&n);
        scanf("%s",s);
        for(int i = 0;i < n; i++)
            s[i+n] = s1[n-i-1] = s1[2*n-i-1] = s[i];
        s[n*2] = s1[n*2] = '\0';
        int pos1 = getMaxStringPos(s,n);
        int pos2 = getMaxnixuPos(s1,n);
        for(int i = 0;i < n; i++) {
            ans1[i] = s[(i+pos1)];
            ans2[i] = s1[(i+pos2)];
        }
        ans1[n] = ans2[n] = '\0';
        if(strcmp(ans1,ans2) > 0) printf("%d 0\n",pos1+1);
        else if(strcmp(ans1,ans2) < 0) printf("%d 1\n",n-pos2);
        else {
            if(n-pos2-1 < pos1) printf("%d 1\n",n-pos2);
            else printf("%d 0\n",pos1+1);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值