[JZOJ5897]密匙--哈希骚操作

[JZOJ5897]密匙--哈希骚操作

题目链接

太懒了自行Google

分析

这题看了样例解释才知道什么意思

本以为自己身为mo法师蛤希已经掌握的差不多,做了这道题才发现我还是Too Young Too Simple

考虑字符集只有\({a,b}\)怎么做,我们每次二分同时判断要不要转化就好了

std就很强了,我们对每一个字母ch,都对\(S\)串跑遍01蛤希(瞎编的名字)

\(ha[i][ch-'a']=ha[i-1][ch-'a']*w+[s[i]==ch]\)

同时再对\(T\)串跑一遍正常的蛤希,\(ha_t[i]=ha_t[i-1]*w + t[i]\)

这时候我们还是进行二分/倍增求LCP,但怎么判断\(S\)串一个前缀能不能得到\(T\)串对应前缀呢

我们先用\(set\)预处理一下,也就是在对应字符那里插入它在\(S\)中出现的位置,这样我们就能较快的求出一个区间内某个字符出现的位置

考虑这时候我们在判断\(S\)\(st\)开始的前缀中\([st,st+len]\)部分能否转化为\(T\)串对应部分

我们还是遍历每一个字母,找到在这个区间内出现的一个位置\(pos\)(如果没有就不管),再判断是否需要映射成\(T\)串对应位置的字符,如果需要而且它之前没有被占用(也就是之前没有出现在字母对中),那就对\(T[pos]\)\(S[pos]\)建立映射关系

同时我们求出这个区间内\(S\)串关于字母\(S[pos]\)的哈希值(如果不知道怎么求的先去学一下\(Rabin-Karp Hash\)),乘上\(T[pos]\)

用一个数\(sum\)累加所有出现字母的这种乘积哈希值,如果没有出现映射矛盾的话,我们会发现如果可以转化成\(T\)串一个前缀,那么这个\(sum\)是会等于\(T\)串对应前缀的哈希值,这是因为\(S\)对应字母的哈希值只有01表示有没有出现,乘上字母的ASIIC值就是该字母在这个区间内的哈希值

代码

/*
  code by RyeCatcher
*/
inline char gc(){
    static char buf[SIZE],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,SIZE,stdin),p1==p2)?EOF:*p1++;
}
#define gc getchar
template <class T>inline void read(T &x){
    x=0;int ne=0;char c;
    while((c=gc())>'9'||c<'0')ne=c=='-';x=c-48;
    while((c=gc())>='0'&&c<='9')x=(x<<3)+(x<<1)+c-48;x=ne?-x:x;return ;
}
const int maxn=100005;
const int inf=0x7fffffff;
ull ha[maxn][28],hat[maxn],mi[maxn];
set <int> pos[28];
int n,m;
int vis[28];
char s[maxn],t[maxn];
bool chk(int l,int r){
	ull tmp=0;
	set<int>::iterator it;
	int x,y;
	memset(vis,-1,sizeof(vis));
	for(ri i=0;i<26;i++){
		it=pos[i].lower_bound(l);
		if(it==pos[i].end()||*it>r)continue;
		x=*it,y=t[x-l+1]-'a';
		if(vis[i]!=-1||vis[y]!=-1){//判断映射是否矛盾
			if(vis[y]!=i||vis[i]!=y){
				return 0;
			}	
		}
		vis[i]=y,vis[y]=i;
		tmp+=(ha[r][i]-ha[l-1][i]*mi[r-l+1])*t[x-l+1];//取出字母的区间哈希值
	}
	if(tmp==hat[r-l+1])return 1;
	return 0;
}
int main(){
	FO(key);
	read(n),read(m);
	scanf("%s",s+1);
	scanf("%s",t+1);
	for(ri k=0;k<26;k++){
		ull tmp=0;
		char o='a'+k;
		for(ri i=1;i<=n;i++){
			if(s[i]!=o)tmp=tmp*31;
			else tmp=tmp*31+1;
			ha[i][k]=tmp;
		}
	}
	mi[0]=1;
	for(ri i=1;i<=n;i++)pos[s[i]-'a'].insert(i),mi[i]=mi[i-1]*31;//预处理
	for(ri i=1;i<=m;i++){
		hat[i]=hat[i-1]*31+t[i];
	}
	for(ri o=1;o<=n;o++){
		int k=0,p=1;
		while(p){
			if(o+k+p<=n&&chk(o,o+k+p))k+=p,p=p<<1;
			else p=p>>1;
			while(o+k+p>n||k+p>m)p=p>>1;
		}
		printf("%d\n",1+k);
	}
	return 0;
}


posted @ 2018-10-18 07:51  Rye_Catcher  阅读(188)  评论(1编辑  收藏  举报