当前位置:   article > 正文

字符串——字符串的最大最小表示法_定义函数求最大字符串和最小字符串

定义函数求最大字符串和最小字符串

字符串的最大最小表示法

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

引言:一个长度为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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

结果

例题

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;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

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;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/310104
推荐阅读
相关标签
  

闽ICP备14008679号