zoukankan      html  css  js  c++  java
  • 哈希

    Seek the Name, Seek the Fame, POJ2752

    description

    给定一个长度为(n) 的串,找出所有的(border) ((nle 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) 字符串,找出其长度最小的循环节。输出循环次数。((nle 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-i));
    				flag=1;break;
    			}
    		if(!flag)puts("1");
    	}
    	return 0;
    }
    

    Stammering Aliens, SWERC 2009, POJ3882

    description

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

    solution

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

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

    time complexity

    (mathcal O(nlog_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
    ",l,ans-1):puts("none");
    	}
    	return 0;
    }
    

    HDU5732 Subway

    description

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

    solution

    树哈希解决树同构问题。

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

    [h_u=size_u imes sum_ih_{son(u,i)} imes A^i+B ]

    其中(A,B) 均为合适的素数,(son(u,i)) 表示(u) 的子树中按(h) 排序从小到大的第(i) 个。

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

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

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

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

    code

    #include<iostream>
    #include<algorithm>
    #include<string>
    #include<map>
    #include<vector>
    #pragma comment(linker, "/STACK:102400000,102400000")
    using namespace std;
    const int N=2e5+5,M=1.5e6+5,base=71,B=233;
    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)
    {
    	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);sz[u]+=sz[v];
    		mx=max(mx,sz[v]);
    	}
    	mx=max(mx,n-sz[u]);
    	if(mx<=n/2)rt1?rt2=u:rt1=u;
    }
    void ghsh(int u,int f)
    {
    	h[u]=0;sz[u]=1;vector<ull>tmp;
    	for(int i=fi[u];i;i=ne[i])
    	{
    		int v=to[i];
    		if(v==f)continue;
    		ghsh(v,u);sz[u]+=sz[v];
    		tmp.push_back(h[v]);
    	}
    	sort(tmp.begin(),tmp.end());
    	for(int i=0;i<tmp.size();++i)h[u]=h[u]*base+tmp[i];
    	h[u]*=tmp.size();h[u]+=B;
    }
    void solve(int u1,int f1,int u2,int f2)
    {
    	cout<<s[u2]<<" "<<s[u1]<<"
    ";
    	vector<pair<ull,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);
    		}
    		rt1=rt2=0;fdrt(1,0);
    		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);
    		}
    		rt1=rt2=0;fdrt(n+1,0);
    		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) 个大写字母串,要求选择尽量多的串使得每个大写字母在其中都出现偶数次。((nle 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(nsqrt 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
    ",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;
    }
    
    NO PAIN NO GAIN
  • 相关阅读:
    java.util.concurrent学习
    mysql慢查优化总结
    mysql怎么限制某些查询语句的执行?
    数据库操作提交事务如果不关闭,会有什么样的后果?
    apache的500错误是写到哪个文件里面
    apache也可以做负载均衡,跟nignx的区别是什么?
    ajax提交请求为啥url要用这个函数encodeURI
    MySQL性能调优与架构设计读书笔记
    java枚举的作用
    linux的命令
  • 原文地址:https://www.cnblogs.com/zmyzmy/p/14642700.html
Copyright © 2011-2022 走看看