为了账号安全,请及时绑定邮箱和手机立即绑定

最大最小表示法

标签:
算法

最大最小表示法

之前学过,然后又忘了。老师说忘记算法是因为你当时学的时候没有认真的思考这个算法。感觉他说的挺对的。所以想写下我对这个算法的理解。
首先,这个算法并不难,但我是想不出这样的算法的。
字符串的最小(大)表示法的问题可以这样描述:
对于一个字符串S,求S的循环的同构字符串S’中字典序最小(大)的一个。
由于语言能力有限,还是用实际例子来解释比较容易:设S=bcad,且S’是S的循环同构的串。S’可以是bcad或者cadb,adbc,dbca。而且最小表示的S’是adbc。
对于字符串循环同构的最小表示法,其问题实质是求S串的一个位置,从这个位置开始循环输出S,得到的S’字典序最小
一种朴素的算法就是找两个指针来记录位置,拿字典序最小来说,令i = 0, j = 1;
如果s[i] > s[j]  i++, j++;
如果s[i] < s[j] j++;
如果s[i] = s[j], 设指针k, k++,向下比较,直到s[i] != s[j]
如果 s[i + k] > s[j + k] i = j;
否则 j++;
但是这个会很浪费时间,比如说这个字符串:bbbbbbbbbba

 b b b b b b b b b b       a i   j             i + k   j + k12

这个时候i = j 就会跑出n^2的复杂度
怎么办呢
举这个栗子

 a b c d    e a b c a b   f i     i+k    j     j+k12

这个时候我们让它调到 i + k + 1的位置, 因为k是已经跑过的了,i不会再去跑k跑过的部分,所以时间复杂度就降到了n。
下面贴一个模板题:

String Problem HDU - 3374

题目链接:https://cn.vjudge.net/contest/163024#problem/O
题目大意:求给定字符串的字典序最小的字符串的首位置,字典序最大的字符串的首位置,以及最小循环节。
input:
abcder
aaaaaa
ababab
output
1 1 6 1
1 6 1 6
1 3 2 3
代码:

#include <bits/stdc++.h>
using namespace std;const int maxn = 1e6 + 10;
char str[maxn];int Next[maxn], len;int min_max_express(bool flag)
{    int i = 0, j = 1, k = 0, t;    while(i < len && j < len && k < len)
    {
        t = str[(i + k) % len] - str[(j + k) % len];        if(!t) k++;        else
        {            if(flag)
            {                if(t > 0) i = i + k + 1;                else j = j + k + 1;
            }            else
            {                if(t > 0) j = j + k + 1;                else i = i + k + 1;
            }            if(j == i) j++;
            k = 0;
        }
    }

    return i < j ? i : j;
}

void get_next()
{    int i, j;    Next[0] = 0;    for(i = 1; i < len; i++)
    {
        j = Next[i - 1];        while(j > 0 && str[i] != str[j])
            j = Next[j - 1];        if(str[i] == str[j])            Next[i] = j + 1;        else
            Next[i] = 0;
    }
}int main()
{    int pos1, pos2, c;    while(~scanf("%s", str))
    {        len = strlen(str);

         pos1 = min_max_express(true);
         pos2 = min_max_express(false);

         get_next();         if(len % (len - Next[len - 1]) == 0)
            c = len / (len - Next[len - 1]);         else
            c = 1;

        printf("%d %d %d %d\n", pos1 + 1, c, pos2 + 1, c);
    }
}

还有一个疑问就是最后为什么要return i < j ? i : j;
感觉直接return i就可以,改了之后往上交代码,发现直接return i也能a,不知道是数据太水还是真的可以直接return i。

原文出处

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消