字符串最小/最大表示法(求最小/最大字典序开始的下标)

解释转自:https://blog.csdn.net/tianyuhang123/article/details/54919715

用于求一个字符串(首尾相连)的最小字典序的下标。

暴力(n*n)

【线性算法】O(N):

初始时,让i=0,j=1,k=0,其中i,j,k表示的是以i开头和以j开头的字符串的前k个字符相同

分为三种情况

1.如果str[i+k]==str[j+k] k++。

2.如果str[i+k] > str[j+k] i = i + k + 1,即最小表示不可能以str[i->i+k]开头。

3.如果str[i+k] < str[j+k] j = j + k + 1,即最小表示不可能以str[j->j+k]开头。

那么只要循环n次,就能够判断出字符串的最小表示是以哪个字符开头。

为什么当str[i+k] > str[j+k] i = i + k + 1,最小表示不可能以str[i->i+k]开头,让我们来举个栗子。

如下图,当i=1,j=5,k=3时,str[i+k] > str[j+k]。

首先有S1S2S3 == S5S6S7,S4 > S8。

那么以字符S2开头肯定不如以字符S6开头更优,因为S4 > S8啊。

最小表示法:

int get_min(string s)
{
    int k=0,i=0,j=1;
    int n=s.size();
    while(k<n&&i<n&&j<n)
    {
        if(s[(i+k)%n]==s[(j+k)%n])
            k++;
        else
        {
            s[(i+k)%n]>s[(j+k)%n]?i=i+k+1:j=j+k+1;
            if(i==j)
                i++;
            k=0;
        }
    }
    i=min(i,j);
    return i;
}

最大表示法: 

int get_max(string s)
{
    int k=0,i=0,j=1;
    int n=s.size();
    while(k<n&&i<n&&j<n)
    {
        if(s[(i+k)%n]==s[(j+k)%n])
            k++;
        else
        {
            s[(i+k)%n]>s[(j+k)%n]?j=j+k+1:i=i+k+1;
            if(i==j)
                i++;
            k=0;
        }
    }
    i=min(i,j);
    return i;
}

基础练习题:zoj 1729   https://vjudge.net/problem/ZOJ-1729

string ss[220];
int get_num(string s)
{
    int k=0,i=0,j=1;
    int n=s.size();
    while(k<n&&i<n&&j<n)
    {
        if(s[(i+k)%n]==s[(j+k)%n])
            k++;
        else
        {
            s[(i+k)%n]>s[(j+k)%n]?i=i+k+1:j=j+k+1;
            if(i==j)
                i++;
            k=0;
        }
    }
    i=min(i,j);
    return i;
}
int main()
{
    int t,cnt;
    scanf("%d",&t);
    while(t--)
    {
        string s;
        cin>>cnt>>s;
        int p=get_num(s);
        printf("%d\n",p);

    }
    return 0;
}

变形题,昨天刚搞的比赛:19牛客暑期多小训练营(第七场)A String  https://ac.nowcoder.com/acm/contest/887/A

题意:

将一个字符串拆分成最少的子串,使得该字串在其本身的循环串(首尾相连)中为字典序最小的那一个。

思路:可以使用最小表示法, 从后向前, 找到当前字典序最小的循环串位置,注意找到的串, 仍要继续判断是否为当前最小循环串, 直到满足条件即可以分割

string ss[220];
int get_num(string s)
{
    int k=0,i=0,j=1;
    int n=s.size();
    while(k<n&&i<n&&j<n)
    {
        if(s[(i+k)%n]==s[(j+k)%n])
            k++;
        else
        {
            s[(i+k)%n]>s[(j+k)%n]?i=i+k+1:j=j+k+1;
            if(i==j)
                i++;
            k=0;
        }
    }
    i=min(i,j);
    return i;
}
int main()
{
    int t,cnt;
    scanf("%d",&t);
    while(t--)
    {
        cnt=0;
        string s;
        cin>>s;
        int len=s.size();
        int i=len;
        for(;;)
        {
            if(i<=0)
                break;
            int k=get_num(s);
            string now=s.substr(k);
            int kn=0;
            while(true)
            {
                kn=get_num(now);
                now=now.substr(kn);
                if(kn==0)
                    break;
            }
            ss[++cnt]=now;
            int len1=now.size();
            i-=len1;
            s=s.substr(0,i);
        }
        for(int i=cnt;i>=1;i--)
        {
            if(i!=1)
                cout<<ss[i]<<" ";
            else
                cout<<ss[i];
        }puts("");
    }
    return 0;
}

2019.8.14补

例题3:hdu-2609

题意:多组输入,每组给你n个字符串,问你有几个不相同的字符串(一个字符串和其循环字符串相同,例如 1001->0011->0110->1100)

思路:利用最小表示法将字符串转换为它的字典序最小的形式,然后将其插入set容器里去重就行了。

#include <bits/stdc++.h>
using namespace std;

int tao(string s)
{
    int k=0,i=0,j=1;
    int n=s.size();
    while(k<n&&i<n&&j<n)
    {
        if(s[(i+k)%n]==s[(j+k)%n])
            k++;
        else
        {
            s[(i+k)%n]>s[(j+k)%n]?i=i+k+1:j=j+k+1;
            if(i==j)
                i++;
            k=0;
        }
    }
    i=min(i,j);
    return i;
}
set<string>st;
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        st.clear();
        string s;
        for(int i=0; i<n; i++)
        {
            cin>>s;
            int p=tao(s);
            string a=s.substr(p)+s.substr(0,p);
            //从p直至串尾  截取p个字符0->p-1
            st.insert(a);
        }
        printf("%d\n",st.size());
    }
}


 

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