码迷,mamicode.com
首页 > 其他好文 > 详细

字符串哈希

时间:2021-04-12 12:18:08      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:gid   rip   second   最小   org   整数   ase   log   正整数   

Seek the Name, Seek the Fame, POJ2752

description

给定一个长度为\(n\) 的串,找出所有的\(border\) \((n\le 400000)\)

solution

直接用哈希模拟判断即可。

code

#include<cstdio>
#include<cstring>
using namespace std;
typedef unsigned long long ull;
const int N=4e5+5,base=101;
char ch[N];int n;ull hsh[N],pw[N];
inline ull ghsh(int l,int r){return hsh[r]-hsh[l-1]*pw[r-l+1];}
int main()
{
	while(scanf("%s",ch+1)!=EOF)
	{
		n=strlen(ch+1);pw[0]=1;
		for(int i=1;i<=n;++i)
			hsh[i]=hsh[i-1]*base+ch[i]-‘a‘+1,pw[i]=pw[i-1]*base;
		for(int i=1;i<=n;++i)
			if(ghsh(1,i)==ghsh(n-i+1,n))printf("%d ",i);
		puts("");
	}
	return 0;
}

Power Strings, POJ2406

description

给定长度为\(n\) 字符串,找出其长度最小的循环节。输出循环次数。\((n\le 10^6)\)

solution

首先套路地将循环转化为\(border\) ,这样只用依次枚举循环节长度用哈希判断即可。

稍微注意下答案为\(1\) 的情况即可。

code

#include<cstdio>
#include<cstring>
using namespace std;
typedef unsigned long long ull;
const int N=1e6+5,base=71;
char ch[N];int n;ull hsh[N],pw[N];
inline ull ghsh(int l,int r){return hsh[r]-hsh[l-1]*pw[r-l+1];}
int main()
{
	while(scanf("%s",ch+1)!=EOF)
	{
		n=strlen(ch+1);
		if(n==1&&ch[n]==‘.‘)break;
		pw[0]=1;
		for(int i=1;i<=n;++i)
			hsh[i]=hsh[i-1]*base+ch[i]-‘a‘+1,pw[i]=pw[i-1]*base;
		bool flag=0;
		for(int i=n-1;i;--i)
			if(n%(n-i)==0&&ghsh(1,i)==ghsh(n-i+1,n))
			{
				printf("%d\n",n/(n-i));
				flag=1;break;
			}
		if(!flag)puts("1");
	}
	return 0;
}

Stammering Aliens, SWERC 2009, POJ3882

description

给定长度为\(n\) 字符串和正整数\(m\) ,要求找出其尽量长的子串满足子串出现次数不小于\(m\) \((n\le 40000)\)

solution

注意到这样一个性质:如果长度为某个\(X+1\) 的子串可行,那么长度为\(X\) 的子串也一定可行。

这样的话就满足单调性。于是可以二分答案\(X\) ,每次将长度为\(X\) 的子串的哈希值都存下来,再排序就可以统计出每个串出现了多少次,于是乎就可以判断是否可行了。

time complexity

\(\mathcal O(n\log_2^2n)\)

code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef unsigned long long ull;
const int N=40005,base=71;
int n,k,ans;char ch[N];ull hsh[N],pw[N];
struct node{int id;ull val;}tmp[N];
inline bool operator<(const node&x,const node&y){return x.val!=y.val?x.val<y.val:x.id<y.id;}
inline ull ghsh(int l,int r){return hsh[r]-hsh[l-1]*pw[r-l+1];}
inline bool ck(int x)
{
	int cnt=0;ans=0;
	for(int i=1;i+x-1<=n;++i)
		tmp[++cnt]={i,ghsh(i,i+x-1)};
	sort(tmp+1,tmp+cnt+1);
	for(int i=1;i<=cnt;)
	{
		int j=i;
		while(j<=cnt&&tmp[j].val==tmp[i].val)++j;
		if(j-i>=k)ans=max(ans,tmp[j-1].id);
		i=j;
	}
	return ans!=0;
}
int main()
{
	while(scanf("%d",&k)==1&&k)
	{
		scanf("%s",ch+1);
		n=strlen(ch+1);pw[0]=1;
		for(int i=1;i<=n;++i)
			hsh[i]=hsh[i-1]*base+ch[i]-‘a‘+1,pw[i]=pw[i-1]*base;
		int l=1,r=n-k+2;
		while(l+1<r)
		{
			int mid=(l+r)>>1;
			ck(mid)?l=mid:r=mid;
		}
		ck(l)?printf("%d %d\n",l,ans-1):puts("none");
	}
	return 0;
}

HDU5732 Subway

description

给出两棵大小为\(n\) 的同构树,要求输出对应的节点。\((n\le 100000)\)

solution

树哈希解决树同构问题。

树哈希是定义在有根树上的,对于树上节点\(u\) ,其哈希值\(h_u\)

\[h_u=1+\sum_{v\in son_u}h_v\times size_v \]

当然这只是树哈希的一种较为通用且较不易出错的方式。

如果两棵树的根\(x,y\) 满足\(h_x=h_y\) 那么我们就认为这两棵树是相同的。

考虑无根树的情况。注意到一棵树至多有两个重心,因此可以把两个重心分别作为根做一遍哈希即可。

输出对应节点的话就采用递归,对儿子哈希值排序,然后递归,这样可以保证儿子的子树仍然是同构的。

code

下面给出一份不知何原因\(RE\) 的代码

#include<iostream>
#include<algorithm>
#include<string>
#include<map>
#include<vector>
using namespace std;
const int N=2e5+5;
typedef unsigned long long ull;ull h[N];
int n,now,rt1,rt2,sz[N],rt;string s[N];
map<string,int>id[2];
inline int gid(const string&ss,bool tp)
{
	if(!id[tp].count(ss))id[tp][ss]=++now,s[now]=ss;
	return id[tp][ss];
}
int tot,fi[N],ne[N<<1],to[N<<1];
inline void add(int x,int y)
{
	ne[++tot]=fi[x],fi[x]=tot,to[tot]=y;
}
void fdrt(int u,int f,int&nmx)
{
	sz[u]=1;int mx=0;
	for(int i=fi[u];i;i=ne[i])
	{
		int v=to[i];
		if(v==f)continue;
		fdrt(v,u,nmx);sz[u]+=sz[v];
		mx=max(mx,sz[v]);
	}
	mx=max(mx,n-sz[u]);
	if(nmx>mx)nmx=mx,rt1=u,rt2=0;
	else if(nmx==mx)rt2=u;
}
void ghsh(int u,int f)
{
	h[u]=1;sz[u]=1;
	for(int i=fi[u];i;i=ne[i])
	{
		int v=to[i];
		if(v==f)continue;
		ghsh(v,u);sz[u]+=sz[v];
		h[u]+=h[v]*sz[v];
	}
}
void solve(int u1,int f1,int u2,int f2)
{
	cout<<s[u2]<<" "<<s[u1]<<"\n";
	vector<pair<int,int> >tmp1,tmp2;
	tmp1.clear(),tmp2.clear();
	for(int i=fi[u1];i;i=ne[i])
	{
		int v=to[i];if(v==f1)continue;
		tmp1.push_back(make_pair(h[v],v));
	}
	for(int i=fi[u2];i;i=ne[i])
	{
		int v=to[i];if(v==f2)continue;
		tmp2.push_back(make_pair(h[v],v));
	}
	sort(tmp1.begin(),tmp1.end());
	sort(tmp2.begin(),tmp2.end());
	for(int i=0;i<tmp1.size();++i)
		solve(tmp1[i].second,u1,tmp2[i].second,u2);
}
inline void clear()
{
	now=0;id[0].clear();id[1].clear();
	tot=0;fill(fi+1,fi+n+n+1,0);
}
int main()
{
	std::ios::sync_with_stdio(false);
	while(cin>>n)
	{
		string tmp;
		for(int i=1,u,v;i<n;++i)
		{
			cin>>tmp;u=gid(tmp,0);
			cin>>tmp;v=gid(tmp,0);
			add(u,v),add(v,u);
		}
		int mx=n+1;rt1=rt2=0;fdrt(1,0,mx);
		ghsh(rt=rt1,0);
		for(int i=1,u,v;i<n;++i)
		{
			cin>>tmp;u=gid(tmp,1);
			cin>>tmp;v=gid(tmp,1);
			add(u,v),add(v,u);
		}
		mx=n+1;rt1=rt2=0;fdrt(n+1,0,mx);
		ghsh(rt1,0);
		if(h[rt1]==h[rt])solve(rt1,0,rt,0);
		else ghsh(rt2,0),solve(rt2,0,rt,0);
		clear();
	}
	return 0;
}

Jurassic Remains, NEERC2003, CF Gym 101388J

description

给出\(n\) 个大写字母串,要求选择尽量多的串使得每个大写字母在其中都出现偶数次。\((n\le 24)\)

solution

出现偶数次让人联想到异或运算。

考虑对于每个串用一个二进制数表示它。二进制的某一位表示当前大写字母出现次数的奇偶性。

问题转化为选择尽量多的数满足其异或和为\(0\)

直接枚举复杂度炸裂,考虑meet in the middle

对于前\(\frac n2\) 个串所能形成的所有异或值,存在\(map\) 中。

然后再枚举后\(\frac n2\) 个串所能形成的所有异或值,每次都在\(map\) 中查询即可。

time complexity

\(\mathcal O(2^{\frac n2}\log_22^{\frac n2})=\mathcal O(n\sqrt 2^n)\)

code

#include<bits/stdc++.h>
using namespace std;
int N,A[30];
char CH[30];
map<int,int>F;
inline int cont(int x)
{
	int anss=0;
	for(int i=0;(1<<i)<=x;++i)
		if(x&(1<<i))++anss;
	return anss;
}
int main()
{
	while(scanf("%d",&N)==1)
	{
		for(int i=0;i<N;++i)
		{
			scanf("%s",CH);
			A[i]=0;
			for(char k=‘A‘;k<=‘Z‘;++k)
				if(strchr(CH,k))A[i]+=(1<<(k-‘A‘));
		}
		int m=N/2,st=1<<m;F.clear();
		for(int i=0;i<st;++i)
		{
			int g=0;
			for(int j=0;(1<<j)<=i;++j)
				if(i&(1<<j))g^=A[j];
			F[g]=(!F.count(g)||cont(i)>cont(F[g]))?i:F[g];
		}
		int n=N-m,stt=1<<n,ans=0,anss=0;
		for(int i=0;i<stt;++i)
		{
			int g=0;
			for(int j=0;(1<<j)<=i;++j)
				if(i&(1<<j))g^=A[j+m];
			if(!F.count(g))continue;
			int ct1=cont(F[g]),ct2=cont(i);
			if(ct1+ct2>ans)
			{
				ans=ct1+ct2;
				anss=F[g]|(i<<m);
			}
		}
		printf("%d\n",ans);
		if(ans)
			for(int i=0;(1<<i)<=anss;++i)
				if(anss&(1<<i))
					printf("%d%s",i+1,(1<<(i+1))>anss?"":" ");
		puts("");
	}
	return 0;
}

字符串哈希

标签:gid   rip   second   最小   org   整数   ase   log   正整数   

原文地址:https://www.cnblogs.com/zmyzmy/p/14642700.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!