字符串——字符串的最大最小表示法

字符串的最大最小表示法

感谢这篇博客:字符串的最小表示法

引言:一个长度为n的字符串可以将最后一位放在第一位,这样就有n种变形。如"bcaxe" 可以变成 :ebcax xebca axebc caxeb 这几种。
字符串的最大最小表示法,就是求着n种变形中的最大最小字典序的字符串,返回的值是第一个最大最小的字符串的起始字符位置,时间复杂度为O(n)。

思想:(最小表示法)
就是定义三个下标初始 i = 0, j = 1, k = 0;
(1) 如果s[i] < s[j] 那么 j++;
(2) 如果s[i] > s[j]明显 i 不是最小,i = j++;
(3) 如果s[i] == s[j], 那么就要用到 k ,以及一个性质,在 i 和 j 之间的字符一定是大于s[i]和s[j]的。令k = 0, 循环寻找第一个s[i+k] != s[j+k]的位置。若 s[i+k] < s[j+k] 那么 j += k+1, 因为s[i+k] < s[j+k] 那么从 i 开始的在i+k位是要比 j 开始的在j+k位更好一些。直接将j += k+1,然后在继续上面的s[i] 和 s[j] 的比较就行。在这里i 和 j 是等价的。 若s[!+k] > s[j+k], 与上述同理,直接令 i += k+1就行。
注意的是 i+k 和 j+k 可能会大于 n, 就要将其 %n。

下面贴代码:

#include <bits/stdc++.h>

using namespace std;

int GetMin(char *s){
    int n = strlen(s);
    int i = 0, j = 1, k = 0, t;
    while(i < n && j < n && k < n){
        t = s[(i+k)%n] - s[(j+k)%n];
        if(!t) k++;
        else{
            if(t > 0) i += k+1;
            else j += k+1;
            if(i == j) j++;
            k = 0;
        }
    }
    return i < j ? i : j;
}

int GetMax(char *s){
    int n = strlen(s);
    int i = 0, j = 1, k = 0, t;
    while(i < n && j < n && k < n){
        t = s[(i+k)%n] - s[(j+k)%n];
        if(!t) k++;
        else{
            if(t > 0) j += k+1;
            else i += k+1;
            if(i == j) j++;
            k = 0;
        }
    }
    return i < j ? i : j;
}


char a[10];

int main()
{
    scanf("%s", a);
    cout << GetMin(a) << " " << GetMax(a) << endl;
    return 0;
}

结果

例题

1.hdu3374 String Problem

链接:hdu3374
题意:多组输入,每组一个字符串,要求输出最小表示法的第一个位置和最小表示法出现的次数 以及 最大表示法的第一个位置和出现次数。
题解:按照上述方法求出最大最小表示法的第一个位置,如何判断出现的次数就要用大KMP的Next数组了,考虑要的到出现的次数,只有当字符串是循环出现的,才有可能在变换后出现相同的字符串。用Next的第一个性质,求出最小循环节tmp = len-Next[len], 判断len%tmp 是否为0,若为0,则表示是循环字符串,最大最小出现次数就是len/tmp。否则出现次数为1。
代码:

const int maxn = 1e6+50;
int m, n;
int Next[maxn];
char str[maxn], mo[maxn];

void GetNext()
{
    m = strlen(mo);
    Next[0] = -1;
    int i = 0, j = -1;
    while(i < m){
        if(j == -1 || mo[i] == mo[j]){
            i++; j++; Next[i] = j;
        }
        else{
            j = Next[j];
        }
    }
    return ;
}

int GetMin(char *s)
{
    n = strlen(s);
    int i = 0, j = 1, k = 0, t;
    while(i < n && j < n && k < n){
        t = s[(i+k)%n] - s[(j+k)%n];
        if(!t) k++;
        else{
            if(t > 0) i += k+1;
            else j += k+1;
            if(i == j) j++;
            k = 0;
        }
    }
    return i < j ? i : j;
}

int GetMax(char *s)
{
    n = strlen(s);
    int i = 0, j = 1, k = 0, t;
    while(i < n && j < n && k < n){
        t = s[(i+k)%n] - s[(j+k)%n];
        if(!t) k++;
        else{
            if(t > 0) j += k+1;
            else i += k+1;
            if(i == j) j++;
            k = 0;
        }
    }
    return i < j ? i : j;
}

int main()
{
    while(scanf("%s", mo) != EOF){
        GetNext();
        int Min = GetMin(mo) + 1;
        int Max = GetMax(mo) + 1;
        int tmp = m - Next[m];
        if(m % tmp == 0 && m != tmp){
            printf("%d %d %d %d\n", Min, m/tmp, Max, m/tmp);
        }
        else{
            printf("%d 1 %d 1\n", Min, Max);
        }
    }
    return 0;
}

2.hdu2609 How many

链接:hdu2609
题意:多组,每组第一行一个n表示该组有n个字符串,每组字符串都是由’0‘和’1‘组成,并且长度相等。现在你可以对任意个字符串进行任意次操作,每次操作是将该字符串的最后一个字符放到最前面,现在让你求出现的最小个数的不同的字符串的个数。
题解:就是说尽可能的将不同的字符串转化为相同字符串,若俩个字符串可以变成一样的,那么他们的最小(大)表示法的字符串一定一样,将每个字符串转化为最小表示法,在用一个set去重就可以了。
代码:

int n, m;
string str;
set<string> Set;

int GetMin(string s){
    m = s.size();
    int i = 0, j = 1, k = 0, t;
    while(i < m && j < m && k < m){
        t = s[(i+k)%m] - s[(j+k)%m];
        if(!t) k++;
        else{
            if(t > 0) i += k+1;
            else j += k+1;
            if(i == j) j++;
            k = 0;
        }
    }
    return i < j ? i : j;
}

int main()
{
    while(scanf("%d", &n) != EOF){
        Set.clear();
        while(n--){
            cin >> str;
            int Min = GetMin(str);
            string tmp = str.substr(Min) + str.substr(0, Min);
            Set.insert(tmp);
        }
        printf("%d\n", Set.size());
    }
    return 0;
}

发布了23 篇原创文章 · 获赞 6 · 访问量 825

猜你喜欢

转载自blog.csdn.net/wxy2635618879/article/details/104028117