字符串最小表示法

参考资料:

https://oi-wiki.org/string/minimal-string/

https://blog.csdn.net/w4149/article/details/76254421


问题:给出一个字符串\(s\),求出这个字符串的所有循环同构串中字典序最小的。

首先有个暴力(直接复制oi-wiki的标):

大概是两个指针\(i,j\)。记\(k\)为公共前缀,尝试扩展\(k\)到不能扩展为止。如果\(k<n\)则可以得到两个指针开头的循环同构串的大小关系,将比较大的右移,继续做。

int k = 0, i = 0, j = 1;
while (k < n && i < n && j < n) {
  if (sec[(i + k) % n] == sec[(j + k) % n]) {
    ++k;
  } else {
    if (sec[(i + k) % n] > sec[(j + k) % n])
      ++i;
    else
      ++j;
    k = 0;
    if (i == j) i++;
  }
}
i = min(i, j);

这个暴力显然是\(O(n^2)\)的。

\(S_{l\dots r}\)一段区间的字符串,\(S_{l\dots l+n-1}\)考虑\(S_{i\dots i+k-1}=S_{j \dots j+k-1}\)\(S_{i+k}>S_{j+k}\),对于任意\(l\in [0,k]\),都有\(S_{i+l\dots i+l+n-1}>S_{j+l\dots j+l+n-1}\)。于是\(i\)可以直接跳到\(i+k+1\),当然由于小于等于\(j\)的都被检验过不是最小的了(否则\(i\)\(j\)必然有个被替换),所以是跳到\(\max(i+k+1,j+1)\)

然后这样就是\(O(n)\)的了。


https://www.cnblogs.com/jz-597/p/13966386.html

这题用最小表示法来判断相等。

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <climits>
#define N 105
#define ll long long
#define mo 998244353
#define jd 19260817
ll qpow(ll x,ll y){
	ll r=1;
	for (;y;y>>=1,x=x*x%mo)
		if (y&1)
			r=r*x%mo;
	return r;
}
int n;
int e[N][N],ne[N];
int re[N][N];
int f[N][N][N];
int vis[N];
int idc[N];
int minidc(int a[],int n){
	int i=0,j=1,k=0;
	while (i<n && j<n){
		for (k=0;k<n && a[(i+k)%n]==a[(j+k)%n];++k)
			k++;
		if (k==n) break;
		if (a[(i+k)%n]>a[(j+k)%n])
			i=max(i+k+1,j+1);
		else
			j=max(j+k+1,i+1);
	}
	i=min(i,j);
	ll res=0;
	for (int j=0;j<n;++j)
		res=(res*jd+a[(i+j)%n])%mo;
	return res;
}
bool same(int x,int y){
	if (ne[x]!=ne[y]) return 0;
	return idc[x]==idc[y];
}
int main(){
//	freopen("in.txt","r",stdin);
	scanf("%d",&n);
	for (int i=1;i<=n;++i){
		scanf("%d",&ne[i]);
		for (int j=0;j<ne[i];++j){
			scanf("%d",&e[i][j]);
			re[i][e[i][j]]=j;
		}
	}
	for (int i=1;i<=n;++i)
		for (int j=0;j<ne[i];++j)
			f[0][i][j]=ne[i];
	for (int k=1;k<=n;++k)
		for (int i=1;i<=n;++i)
			for (int j=0;j<ne[i];++j){
				int y=e[i][j];
				ll key=0;
				for (int t=re[y][i];t<ne[y];++t)
					key=(key*jd+f[k-1][y][t])%mo;
				for (int t=0;t<re[y][i];++t)
					key=(key*jd+f[k-1][y][t])%mo;
				f[k][i][j]=key;
			}
	for (int i=1;i<=n;++i)
		idc[i]=minidc(f[n][i],ne[i]);
//	for (int i=1;i<=n;++i){
//		printf("i=%d : %d\n",i,idc[i]);
//		for (int j=0;j<ne[i];++j)
//			printf("%d ",f[n][i][j]);
//		printf("\n");
//	}
	int bz=0;
	for (int i=1;i<=n;++i)
		if (!vis[i]){
			vis[i]=1;
			static int q[N];
			int k=1;
			q[1]=i;
			for (int j=i+1;j<=n;++j)
				if (!vis[j] && same(i,j)){
					vis[j]=1;
					q[++k]=j;
				}
			if (k>1){
				for (int j=1;j<=k;++j)
					printf("%d ",q[j]);
				printf("\n");
				bz=1;
			}
		}
	if (bz==0)
		printf("none\n");
	return 0;
}
posted @ 2020-11-12 22:00  jz_597  阅读(151)  评论(0编辑  收藏  举报