最小表示法

今天 LJ 课上讲了最小表示法。感觉很好理解。
决定写一篇博客,自己梳理一下。
最小表示法是

\[\texttt{对于一个字符串S,求其循环同构字符串中S'中字典序最小的一个} \]

我们可以定义三个指针 \(i,j,k\)
其中 \(i=0,j=1,k=0\)\(k\) 表示以 \(S_i\) 开头的字符串和以 \(S_j\) 开头的字符串的前 \(k\) 个字符相同。
然后我们枚举 \(k\)
\(S_{i+k}=S_{j+k}\) 则继续。
\(S_{i+k}>S_{j+k}\) 则表明 \(S_{[i,i+k]}\) 肯定不可以作为目标字符串的开头。
\(S_{i+k}<S_{j+k}\) 则同理。
\(i=j\) 时,令 \(j+1\)
后面三种情况都要使 \(k\)\(0\)
代码很好写

P1368 【模板】最小表示法

#include<bits/stdc++.h>
#define FOR(i,j,k)  for(int i=(j);i<=(k);i++)
using namespace std;
int n;
int a[300005];
inline int read()
{
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*f;
}
inline int Min()
{
	int i=0,j=1,k=0;
	while(i<n&&j<n&&k<n)
	{
		int t=a[(i+k)%n]-a[(j+k)%n];
		if(!t)	k++;
		else
		{
			if(a[(i+k)%n]>a[(j+k)%n])	i+=k+1;
			else j+=k+1;
			if(i==j)	j++;
			k=0;
		}
	}
	return min(i,j);
}
int main()
{
	n=read();
	for(int i=0;i<n;i++)	a[i]=read();
	int ans=Min();
	for(int i=0;i<n;i++)
		printf("%d ",a[(i+ans)%n]);
	return 0;
}

当然他也可以用来判断两个字符串不计顺序是否本质相同。详细可以参见这个PPT

posted @ 2020-11-28 11:50  LJC001151  阅读(53)  评论(0)    收藏  举报