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
  • 相关阅读:
    poj 2584 T-Shirt Gumbo (二分匹配)
    hdu 1757 A Simple Math Problem (乘法矩阵)
    矩阵之矩阵乘法(转载)
    poj 2239 Selecting Courses (二分匹配)
    hdu 3661 Assignments (贪心)
    hdu 1348 Wall (凸包)
    poj 2060 Taxi Cab Scheme (二分匹配)
    hdu 2202 最大三角形 (凸包)
    hdu 1577 WisKey的眼神 (数学几何)
    poj 1719 Shooting Contest (二分匹配)
  • 原文地址:https://www.cnblogs.com/zmyzmy/p/14642700.html
Copyright © 2011-2022 走看看