Loading

最小表示法

1 最小表示法一般求解方法:

我们采用双指针的方法去求最小表示,在下面的代码中 \(k\) 为字符串长度,\(i,j\) 分别为两个字符串的开始节点,容易知道,如果两个字符串失配了,那么其中那个大的失配的,假设是 \(i\) ,那么 \(i+k-1\) 都不可能成为答案了。因为一定存在一个字符串比他更优。所以我们直接跳指针就可以。注意要特判 \(i,j\) 相等的情况。

最后肯定 \(i,j\) 有一个越界,那么小的那个就是最小表示法。

因为双指针不往左走,所以复杂度 \(O(n)\)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<sstream>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<deque>
#include<cstdlib>
#include<ctime>
#define dd double
#define ld long double
#define ll long long
#define ull unsigned long long
#define N 10010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

inline int Min(int a,int b){
    return a<b?a:b;
}

inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}

int n;

int main(){
    n=read();
    while(n--){
        char s[N];
        scanf("%s",s);
        int i=0,j=1,k=0,len=strlen(s);
        while(i<len&&j<len&&k<len){
            if(s[(i+k)%len]==s[(j+k)%len]) k++;
            else{
                s[(i+k)%len]>s[(j+k)%len]?i=i+k+1:j=j+k+1;
                if(i==j) i++;
                k=0;
            }
        }
        i=Min(i,j);
        printf("%d\n",i+1);
    }
    return 0;
}

利用双指针,这个不做过多讲解。

2Lyndon分解求解最小表示法

设长度为\(t\)的字符串\(s\)

我们只需要求解字符串\(ss\)的Lyndon分解,找到一个左端点在\(n\)以内,右端点在\(n\)右边的字符串,那么从左端点开始,长度为\(n\)\(ss\)的字串,就是最小表示法。

证明:由于Lyndon分解是唯一的,所以满足上面条件的字符串也有且只有一个。我们需要证明它是字符串的最小表示。

设字符串\(ss\),满足上面条件的字符串\(t\)起点为\(i\),终点为\(j\),那么1到\(i-1\)的Lyndon分解出来的字符串根据定义一定大于\(t\),且这些分解的循环同构串也一定大于\(t\),故起点为1到\(i-1\)的循环同构串一定大于\(t\)。因为字符串\(t\)为Lyndon串,所以起点在\(i+1\)\(n\)的循环同构串也大于\(t\),综上,t为最小表示。

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<sstream>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<deque>
#include<cstdlib>
#include<ctime>
#define dd double
#define ld long double
#define ll long long
#define ull unsigned long long
#define N 5000010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

inline int Min(int a,int b){
    return a<b?a:b;
}

inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}

int n;
char s[N];

int main(){
    scanf("%d",&n);
    while(n--){
        scanf("%s",s);
        int len=strlen(s),ans=0;
        for(int i=len;i<len*2;i++) s[i]=s[i-len];
        len*=2;
        for(int i=0;i<len/2;){
            ans=i;
            int j=i,k=i+1;
            while(k<len&&s[j]<=s[k]){
                if(s[j]<s[k]) j=i;
                else j++;
                k++;
            }
            while(i<=j){
//                if(i<len/2&&i+k-j>=len/2) ans=i;
                i+=k-j;
            }
        }
        printf("%d\n",ans+1);
    }
    return 0;
}
posted @ 2021-04-20 16:51  hyl天梦  阅读(212)  评论(0编辑  收藏  举报