牛客练习赛36 A. Rabbit的字符串——最小(大)表示法

链接:

Rabbit的字符串

题目描述:

Rabbit得到了一个字符串,她的好朋友xxx可以给这个字符串施加一次魔法。
魔法可以选择字符串的任一位置,并将该位置后面的所有字符水平拼接到串首。
例如:对于字符串abcde,可以通过施加魔法得到cdeab。
如果xxx通过施加魔法将字符串的字典序变得严格比之前的小,那么他将拿走这一字符串。
Rabbit想知道自己的字符串会不会被xxx拿走。

思路:

这次练习赛的签到题,最小表示法裸题,知识点不难,以前也有见过,只是不太常见生疏了,拿出来理一下思路,顺便回顾一下。

算法:

求解环形字符串从哪个位置开始字典序最小(大)时,暴力算法枚举起点,再遍历一次字符串,复杂度O(n^2),实在不能令人满意。
最小(大)表示法通过引入i,j,l三个变量,其中i,j充当指针作用,用来表示"从位置i(j)开始的字符串字典序最小(大)",l则是从位置i和位置j开始的字符串,有连续长度为l位相同。
即:str[i]->str[i+l] == str[j]->str[j+l]
最小表示法为例,一开始,令i = 0,j = 1,l根据实际情况得出,每次计算出l之后会有以下几种情况:

①l == len:意为从第i位开始的串和第j位开始的环形串字典序相同(如"aaaaa"),这时直接返回结果即可(i/j均可,不过一般除了SPJ答案唯一,一般都要比较小的)

思考:为什么从环形字符串两个不同位置开始的字符串完全相同,从这两个位置开始的字符串字典序就最小?
因为出现这种情况的串一定是所有字符均相同的串。
假设该环形字符串所有字符不全相同,则从两个不同位置i,j开始的串里,该字符所在的位置不同。
举例:aaabaaaa
从0开始的串:aaabaaaa
从1开始的串:aabaaaaa
与我们的假设——“从两个位置开始的字符串相同”相违背。

②str[i+l+1] > str[j+l+1]:从i - > max(i+l,j-1)里面不可能产生最佳答案。
为什么不能在[i,i+l]里产生?
假设s1 s2 s3和s7 s8 s9相同,s4 > s10
那么从s2开始的串s2 s3 s4和s8 s9 s10相比会面临相同的问题:s4 > s10,这一问题没有改变。
为什么不能在[i,j-1]里产生?
i和j都是从小往大增上去的,也就是说j已经走过[i,j-1]这段路并证明这段路非最优了。
令i = max(i+l+1,j+1)

③str[i+l+1] < str[j+l+1]:从j -> max(j+l,i-1)里面不可能产生最佳答案。
令j = max(j+l+1,i+1)

最后,当i或者j >= len的时候,就可以结束循环了。
这时候返回i和j里面比较小的一个就是答案了。
为什么是比较小的一个?
因为每次循环改变的i,j都是不优的。考虑最后一次i和j的变化,是它们的变大使得i < len && j < len这个条件不成立了,那就是意味着 < len的一个是最优答案。

代码:

//最小表示法、最大表示法 O(n) 返回从某一位开始的字典序序列最小/最大 
int getMin(const char& str){
    int len = strlen(str);
    int i = 0,j = 1;
    while (i < len && j < len){
        int l = 0;
        while(l < len)
        if(str[(i + l) % len] != str[(j + l) % len])break;
        else l++;
        if(l >= len)break;
        if(str[(i + l) % len] > str[(j + l) % len]){
            if (i + l + 1 > j)i = i + l + 1;
            else i = j + 1;
        }
        else if (j + l + 1 > i) j = j + l + 1; 
        else j = i + 1;
    }
    return i < j ? i : j;
}

int getMax(int len) {
    int i = 0, j = 1, k = 0;
    while (i < len && j < len && k < len) {
        int t = str[(i + k) % len] - str[(j + k) % len];
        if (!t) k++;
        else {
            if (t > 0) {
                if (j + k + 1 > i) j = j + k + 1;
                else j = i + 1;
            }
            else if (i + k + 1 > j) i = i + k + 1;
            else i = j + 1;
            k = 0;
        }
    }
    return i < j ? i : j;
}

其他应用场景:

最小(大)表示法可以将一个环形字符串唯一确定地表示(从开始位置跑一遍就是了),可据此定义环形字符串的大小比较运算。

猜你喜欢

转载自blog.csdn.net/krypton12138/article/details/85803497