牛客練習賽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;
}

其他應用場景:

最小(大)表示法可以將一個環形字符串唯一確定地表示(從開始位置跑一遍就是了),可據此定義環形字符串的大小比較運算。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章