【题解】Bzoj3916

字符串\(Hash\).

笔者实在太菜了,到现在还没有熟练掌握\(Hash\),就来这里写一篇学习笔记。

\(Description\)

有三个好朋友喜欢在一起玩游戏,\(A\)君写下一个字符串\(S,B\)君将其复制一遍得到\(T,C\)君在T的任意位置(包括首尾)插入一个字符得到\(U\).现在你得到了\(U\),请你找出\(S.\)

\(Input\)

第一行一个数\(N\),表示\(U\)的长度.

第二行一个字符串\(U\),保证\(U\)由大写字母组成

\(Output\)

输出一行,若S不存在,输出"NOT POSSIBLE".若\(S\)不唯一,输出"NOT UNIQUE".否则输出\(S.\)

字符串\(Hash\),可以快速做到字符串匹配。比如,从一个字符串中选择两个子串进行匹配,还是要\(Hash\)的。

\(Hash\)是一种随机算法。我们设计一个\(Hash\)函数:

设字符串\(C=c_1c_2...c_n\)\(K\)为字符串的长度(前\(K\)个)

选择两个数\(b,h\),满足\(b<h,b\)\(h\)互质。

我们有:

\(Hash(C,K)=Hash(C,K-1)*b+c_k\)

所以,我们的复杂度瓶颈就在处理\(b\)上了。

对上面的函数拆开来写,就是:

\(x=m-1\),

\(Hash(C)=(c_1*b^x+...+c_m*1) mod h\)

预处理\(b\)的次幂,递推即可。

考虑一下\(Hash\)冲突的问题。

我们前面取了一个模数\(h=Mod\)对吧。后面我写成\(Mod\).

显然,对于任意串的\(Hash\)值一定是模\(Mod\)意义下剩余系中的一个,可以认为在区间\([0,Mod-1]\)中随机分布。

考虑几个串不相等。

对于第一个串,有\(\frac{Mod-1}{Mod}\)的概率。

如果第二个要和第一个不相等,就只有\(\frac{Mod-2}{Mod}\)的概率。

以此类推,所有串不相等的概率为:

\(\frac{Mod-1}{Mod}*\frac{Mod-2}{Mod}*...*\frac{Mod-i+1}{Mod}\)

\(i\)是子串数(枚举)。

当我们把模数\(1e9\),字符串数达到\(1e5\)的时候,不冲突的概率为:

\(0.006737160911819992\)

在这么大的数据下,显然冲突的概率非常高。如何提高它不冲突的概率?

我们选择模数的时候,发现模数越大,冲突概率越小。那我们就可以利用\(unsigned long long\),自然溢出即可。

还可以双\(Hash\),然而笔者太菜,不会,就先不说了,其实就是在就算一遍\(Hash\)后再计算几遍,只有\(Hash\)值全部相等才算匹配成功。

考虑单\(Hash\)

这道题,我们的思路就是,枚举断点,然后计算前后两个子串的\(Hash\)是否匹配。

取一段子串的\(Hash\)值时,记住:

当其起点为\(i\),终点为\(j\)时(子串),

\(Hash=H[r]-H[l-1]*power[r-l+1]\)

其中\(H\)数组是计算主串\(Hash\)时计算出来的。

这道题注意一下细节即可。判重的时候,如果与前面有一样的答案,算一个。除非是答案中的字符串不同,才算多重答案。

\(Code:\)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
using namespace std;
typedef unsigned long long ull;
const int B=2333333;
const int H=1e9+9;
int n,vis,L;
ull power[2000010],h[2000010],last;
char s[2000010],a[2000010];
ull Get(int l,int r){return h[r]-h[l-1]*power[r-l+1];}
bool check(int pos){
	ull x,y,z;
	int flag=0;
	if(pos<L){
		x=Get(1,pos-1)*power[L-pos]+Get(pos+1,L);
		y=Get(L+1,n);
	}
	else if(pos>L){
		x=Get(1,L-1);
		y=Get(L,pos-1)*power[n-pos]+Get(pos+1,n);
	}
	else if(pos==L){
		x=Get(1,L-1);
		y=Get(L+1,n);
	}
	if(x==y){
		if(x==last)return 0;
		last=x;
		int top=0;
		if(pos<=L)
			for(int i=L+1;i<=n;++i)
				a[++top]=s[i];
		else
			for(int i=1;i<=L-1;++i)
				a[++top]=s[i];
		return true;
	}
	return 0;
}
int main(){
	scanf("%d",&n);
	scanf("%s",s+1);
	power[0]=1;
	if(n%2==0){
		printf("NOT POSSIBLE\n");
		return 0;
	}
	for(int i=1;i<=n;++i)
		power[i]=power[i-1]*B;
	for(int i=1;i<=n;++i)
		h[i]=h[i-1]*B+s[i];
	L=(n>>1)+1;
	for(int i=1;i<=n;++i){
		vis+=check(i);
		if(vis>1)break;
	}
	if(!vis){
		printf("NOT POSSIBLE\n");
		return 0;
	}
	else if(vis>1){
		printf("NOT UNIQUE\n");
		return 0;	
	}
	else puts(a+1);
	return 0;
} 
posted @ 2019-08-07 20:53  Refined_heart  阅读(320)  评论(1编辑  收藏  举报