BZOJ 1398: Vijos1382寻找主人 Necklace(最小表示法)

传送门

解题思路

  最小表示法。首先对于判断是不是循环同构的串,直接扫一遍用哈希判即可。然后要输出字典序最小的就要用到最小表示法,首先可以把串复制一遍,这样的话就可以把串变成静态操作。如果对于两个位置\(i,j\),若他们\(i\sim i+k-1\)\(j\sim j+k-1\)这些位置都两两相等,而\(s[i+k]<s[j+k]\)的话。那么首先可以知道的是\(j\)这个位置一定不是最小表示的开头,还有一个性质就是\(j\sim j+k\)这些位置也一定不是,因为\(s[i+k]<s[j+k]\),那么这些位置也一定有一个对应的\(i_0\),使得他们在\(i+k\)的位置依然不相等。所以用一个双指针扫,时间复杂度\(O(n)\)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>

using namespace std;
const int MAXN = 1000005;
const int base = 666623333;
typedef unsigned long long ULL;

char s1[MAXN],s2[MAXN];
ULL hsh1[MAXN<<1],hsh2[MAXN],poww[MAXN<<1];
int ans,n;
bool flag;

inline bool check(int x){
	return (hsh1[x+n-1]-hsh1[x-1]*poww[n])==(hsh2[n]-poww[n])?1:0;
}

int main(){
	scanf("%s%s",s1+1,s2+1);
	n=strlen(s1+1);
	for(int i=1;i<=n;i++) s1[i+n]=s1[i];hsh1[0]=hsh2[0]=1;poww[0]=1;
	for(int i=1;i<=(n<<1);i++) {hsh1[i]=hsh1[i-1]*base+s1[i]-'0'+1;poww[i]=poww[i-1]*base;}
	for(int i=1;i<=n;i++) hsh2[i]=hsh2[i-1]*base+s2[i]-'0'+1;
	for(int i=1;i<=n;i++) if(check(i)) {flag=1;break;}
	if(!flag) {puts("No");return 0;}
	puts("Yes");int l=1,r=2,k;
	while(l<=n && r<=n) {
		for(k=0;s1[l+k]==s1[r+k] && k<=n;k++);
		if(k==n) break;
		if(s1[l+k]<s1[r+k]) {r=r+k+1;r+=(r==l);}
		else {l=l+k+1;l+=(l==r);}
	}
	ans=min(l,r);
	for(int i=ans;i<=ans+n-1;i++) putchar(s1[i]);putchar('\n');
	return 0;
}
posted @ 2018-10-25 09:03  Monster_Qi  阅读(243)  评论(0编辑  收藏  举报