最大最小表示法

最大最小表示法

之前学过,然后又忘了。老师说忘记算法是因为你当时学的时候没有认真的思考这个算法。感觉他说的挺对的。所以想写下我对这个算法的理解。
首先,这个算法并不难,但我是想不出这样的算法的。
字符串的最小(大)表示法的问题可以这样描述:
对于一个字符串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 + k

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

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

这个时候我们让它调到 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。

猜你喜欢

转载自blog.csdn.net/deerly_/article/details/79967115