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

解釋轉自: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());
    }
}


 

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