紫薯P52 例题3-6

一、题意

有kase个循环字符串,输出每个字符串 (长度n<=100) 的最小表示。

二、解析

经典的求最小循环字符串题目,这里用来复习一下它的一种O(1)算法。

基本思想是双指针i, j,用k表示从i、j开始的子串的当前比对到的完全一样的长度,当判断出i、j的某一条子串的i+k、j+k位置有所不同时,大的那一条(比如是j)就可以往后跳k+1个字符,因为以这些字符起始的循环字符串已经不可能是最小的(比它们小的在i开始的串的子串中)。用这样的方法当i、j串中的某一个超过n时就不用再判断了(因为n个可能的起始位置都判断过了)。此时剩余的那个串就是最小的循环字符串。

三、代码

#include <iostream>
#include <string>
#include <cmath>
using namespace std;
int kase, n;
string str;

int solve() {
    int i = 0, j = 1, k = 0;
    while(i < n && j < n) {
        int tmp = str[(i + k) % n] - str[(j + k) % n];
        if(tmp == 0) k ++;
        else if(tmp < 0) j += k + 1, k = 0;
        else i += k + 1, k = 0;
        if(i == j) j ++, k = 0;
    }
    return min(i, j);
}

int main() {
    cin >> kase;
    while(kase --) {
        cin >> str;
        n = str.size();
        int idx = solve();
        cout << str.substr(idx) + str.substr(0, idx) << endl;
    }
}

/*
2
CGAGTCAGCT
CTCC
*/

四、归纳

  • 经典算法可以记一下:O(1)时间求最小循环字符串

差点写不出来...