1. 程式人生 > 實用技巧 ><img>標籤

<img>標籤

今天 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