字符串\(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}\)的概率。
以此类推,所有串不相等的概率为:
\(\farc{Mod-1}{Mod}\)\(farc{Mod-2}{Mod}\)...*\(farc{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; }