[总结] 字符串的最小表示

[总结] 字符串的最小表示

给定一个字符串 \(S\),如果不断把它的最后一个字符放到开头,会形成 \(n\) 个字符串,它们循环同构

这些字符串中字典序最小的,叫做字符串 \(S\) 的最小表示。

算法分析

POJ1509 Glass Beads

板子题

对于如何构造,首先要先把字符串复制一倍,设 \(B[i]\) 表示 \(S[i-i+n-1]\),则每一个 \(B[i]\) 都是一个字符串的循环同构串的表示。

算法流程基于 双指针 思想:

  1. 维护两个指针 \(i\)\(j\),初始时令 \(i=1,j=2\) ,分别表示维护的两个循环同构串。
  2. 维护一个值 \(k\),表示在 \(k\) 这个距离 \(S[i+k]\not =s[j+k]\),分两种情况考虑:
    1. \(S[i+k]<s[j+k]\),说明 \(i\) 更优,并且 \(j-j+k\) 都不可能成为最小表示的开头了,那么令 \(j=j+k+1\)
    2. 否则令 \(i=i+k+1\)

注意:如果跳转过程中出现了 \(i=j\) 的情况,那么令跳跃的那一方再进 \(1\)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
template <typename T>
inline T read(){
	T x=0;char ch=getchar();bool fl=false;
	while(!isdigit(ch)){if(ch=='-')fl=true;ch=getchar();}
	while(isdigit(ch)){
		x=(x<<3)+(x<<1)+(ch^48);ch=getchar();
	}
	return fl?-x:x;
}
const int maxn = 1e4 + 10;
char s[maxn<<1];
int T,ans;
int main(){
	T=read<int>();
	while(T--){
		cin>>s+1;
		int n=strlen(s+1);
		for(int i=1;i<=n;i++)s[n+i]=s[i];
		int i=1,j=2,k=0;
		while(i<=n && j<=n){
			for(k=0;k<n && s[i+k]==s[j+k];k++);
			if(k==n)break;
			if(s[i+k]>s[j+k]){
				i=i+k+1;if(i==j)i++;
			}
			else{
				j=j+k+1;if(i==j)j++;
			}
		}
		ans=min(i,j);
		printf("%d\n",ans);
	}
	return 0;
}
posted @ 2021-08-12 18:31  ¶凉笙  阅读(249)  评论(0编辑  收藏  举报