最小表示法
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;
}