蓝书2.1 哈希和哈希表

T1 Power Strings    poj 2406

题目大意:

求每个字符串的最短循环节的循环次数

思路:

哈希用kmp做

直接判断一下n-nxt[n]是否是n的约数

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<cstdlib>
 5 #include<cstring>
 6 #include<algorithm>
 7 #include<vector>
 8 #include<queue>
 9 #define inf 2139062143
10 #define ll long long
11 #define MAXN 100100100
12 using namespace std;
13 inline int read()
14 {
15     int x=0,f=1;char ch=getchar();
16     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
17     while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
18     return x*f;
19 }
20 int n,nxt[MAXN];
21 char a[MAXN];
22 int main()
23 {
24     while(scanf("%s",a+1))
25     {
26         if(a[1]=='.') break;
27         n=strlen(a+1);
28         for(int i=1,j=0;i<n;i++)
29         {
30             while(a[i+1]!=a[j+1]&&j) j=nxt[j];
31             if(a[i+1]==a[j+1]) j++;
32             nxt[i+1]=j;
33         }
34         if(n%(n-nxt[n])==0) printf("%d\n",n/(n-nxt[n]));
35         else puts("1");
36     }
37 }
View Code

 

T2 Seek the name ...  poj 2752

题目大意:

求每个字符串的所有border

思路:

用nxt[n]一直往前跳nxt就行了

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<cstdlib>
 5 #include<cstring>
 6 #include<algorithm>
 7 #include<vector>
 8 #include<queue>
 9 #define inf 2139062143
10 #define ll long long
11 #define MAXN 400100
12 using namespace std;
13 inline int read()
14 {
15     int x=0,f=1;char ch=getchar();
16     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
17     while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
18     return x*f;
19 }
20 int n,nxt[MAXN];
21 char a[MAXN];
22 void work(int x) {if(!x)return ;work(nxt[x]);printf("%d ",x);}
23 int main()
24 {
25     while(scanf("%s",a+1)!=EOF)
26     {
27         n=strlen(a+1);
28         for(int i=1,j=0;i<n;i++)
29         {
30             while(a[i+1]!=a[j+1]&&j) j=nxt[j];
31             if(a[i+1]==a[j+1]) j++;
32             nxt[i+1]=j;
33         }
34         work(n);puts("");
35     }
36 }
View Code

 

T3  friends bzoj 3916

题目大意:

字符串s复制一遍后加入一个字符x得到字符串t

已知t 求s的个数

思路:

枚举插入的位置 字符串哈希判断

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<cstdlib>
 5 #include<cstring>
 6 #include<algorithm>
 7 #include<vector>
 8 #include<queue>
 9 #include<map>
10 #define inf 2139062143
11 #define ll long long
12 #define MAXN 2000100
13 const int MOD=1e9+7;
14 using namespace std;
15 inline int read()
16 {
17     int x=0,f=1;char ch=getchar();
18     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
19     while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
20     return x*f;
21 }
22 map <int,int> vis;
23 int n,ans,t;
24 ll hsh[MAXN],p[MAXN];
25 char ch[MAXN];
26 ll Minus(ll a,ll b) {return (a-b)%MOD>0?(a-b)%MOD:(a-b)%MOD+MOD;}
27 int main()
28 {
29     n=read(),p[0]=1;
30     scanf("%s",ch+1);
31     if(!(n&1)) {puts("NOT POSSIBLE");return 0;}
32     for(int i=1;i<=n;i++) p[i]=(p[i-1]*137)%MOD,hsh[i]=(ch[i]-'A'+1+hsh[i-1]*137%MOD)%MOD;
33     for(int i=1,x,y;i<=n;i++)
34     {
35         if(i<=n/2+1) x=(hsh[i-1]*p[n/2-i+1]%MOD+Minus(hsh[n/2+1],hsh[i]*p[n/2+1-i]))%MOD,y=Minus(hsh[n],hsh[n/2+1]*p[n/2]);
36         else x=hsh[n/2],y=(Minus(hsh[i-1],hsh[n/2]*p[i-n/2-1])*p[n-i]%MOD+Minus(hsh[n],hsh[i]*p[n-i]))%MOD;
37         if(x==y&&!vis[x]) ans++,t=i,vis[x]=1;
38     }
39     if(!ans) puts("NOT POSSIBLE");
40     else if(ans>1) puts("NOT UNIQUE");
41     else if(t>=n/2+1) for(int i=1;i<=n/2;i++) printf("%c",ch[i]);
42     else for(int i=n/2+2;i<=n;i++) printf("%c",ch[i]);
43 }
View Code

 

T4 A Horrible Poem bzoj 2795

题目大意:

q次询问 每次询问字符串的一段子串的最短循环节

思路:

很容易想到 q sqrt(n) 枚举每段长度的所有约数 O1 判断

但是感觉太捞了

于是可以分解质因数 用线筛求出每个数最小的质因数

不断枚举质因数 用长度判断

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<cstdlib>
 5 #include<cstring>
 6 #include<algorithm>
 7 #include<vector>
 8 #include<queue>
 9 #include<map>
10 #define inf 2139062143
11 #define ll long long
12 #define MAXN 500100
13 const int MOD=1e9+7;
14 using namespace std;
15 inline int read()
16 {
17     int x=0,f=1;char ch=getchar();
18     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
19     while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
20     return x*f;
21 }
22 ll Minus(ll a,ll b) {return (a-b)%MOD>0?(a-b)%MOD:(a-b)%MOD+MOD;}
23 int n,T,ntp[MAXN],p[MAXN],cnt;
24 char ch[MAXN];
25 ll hsh[MAXN],pw[MAXN];
26 void mem()
27 {
28     for(int i=2;i<MAXN;i++)
29     {
30         if(!ntp[i]) ntp[i]=i,p[++cnt]=i;
31         for(int j=1;p[j]*i<MAXN;j++)
32         {
33             ntp[p[j]*i]=p[j];
34             if(i%p[j]==0) break;
35         }
36     }
37 }
38 int main()
39 {
40     n=read(),pw[0]=1;int a,b,len;
41     scanf("%s",ch+1);
42     for(int i=1;i<=n;i++) pw[i]=(pw[i-1]*137)%MOD,hsh[i]=(ch[i]-'a'+1+hsh[i-1]*137%MOD)%MOD;
43     T=read();mem();
44     while(T--)
45     {
46         a=read(),b=read(),len=b-a+1;
47         for(int i=len,c;i>1;)
48         {
49             c=ntp[i];
50             while(len%c==0&&Minus(hsh[b-len/c],hsh[a-1]*pw[b-len/c-a+1])==Minus(hsh[b],hsh[a+len/c-1]*pw[b-a-len/c+1])) len/=c;
51             while(i%c==0) i/=c;
52         }
53         printf("%d\n",len);
54     }
55 }
View Code

 

T5 Beads bzoj 2081

题目大意:

求一些长度 使这些紧挨着的长度的子串有多少个不同的(前后翻转算一个)

思路:

暴力枚举长度 由调和级数可得总复杂度为 nlogn

然后使用set 和哈希求出多少个不同的子串即可

卡1e9+7 但没卡自然溢出是什么鬼

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<cstdlib>
 5 #include<cstring>
 6 #include<algorithm>
 7 #include<vector>
 8 #include<queue>
 9 #include<set>
10 #define inf 2139062143
11 #define ll long long
12 #define MAXN 200100
13 const int MOD=98754321;
14 using namespace std;
15 inline int read()
16 {
17     int x=0,f=1;char ch=getchar();
18     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
19     while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
20     return x*f;
21 }
22 set <int> s;
23 int n,ans,ar[MAXN],cnt,g[MAXN],hsh[MAXN],HSH[MAXN],pw[MAXN];
24 char ch[MAXN];
25 void calc(int x)
26 {
27     s.clear();
28     for(int i=1,a,b;i+x-1<=n;i+=x)
29     {
30         a=hsh[i+x-1]-hsh[i-1]*pw[x],b=HSH[i]-HSH[i+x]*pw[x];
31         s.insert(min(a,b));
32     }
33     if(s.size()==ans) ar[++cnt]=x;
34     if(s.size()>ans) ans=s.size(),ar[cnt=1]=x;
35 }
36 int main()
37 {
38     n=read();
39     for(int i=pw[0]=1;i<=n;i++) g[i]=read(),pw[i]=pw[i-1]*19260817,hsh[i]=g[i]+hsh[i-1]*19260817;
40     for(int i=n;i;i--) HSH[i]=g[i]+HSH[i+1]*19260817;
41     for(int i=1;i<=n;i++) calc(i);
42     printf("%d %d\n%d",ans,cnt,ar[1]);
43     for(int i=2;i<=cnt;i++) printf(" %d",ar[i]);
44 }
View Code

 

T6 Antisymmetry bzoj 2084

题目大意:

对一个01字符串,如果将这个字符串0和1取反后,再将整个串反过来和原串一样,就称作“反对称”字符串

现在给出一个长度为N的01字符串,求它有多少个子串是反对称的

思路:

枚举每个中心

然后二分长度 哈希判断 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<cstdlib>
 5 #include<cstring>
 6 #include<algorithm>
 7 #include<vector>
 8 #include<queue>
 9 #include<set>
10 #define inf 2139062143
11 #define ll long long
12 #define MAXN 500100
13 const int MOD=1e9+7;
14 const int bas=2333;
15 using namespace std;
16 inline int read()
17 {
18     int x=0,f=1;char ch=getchar();
19     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
20     while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
21     return x*f;
22 }
23 int n;
24 ll hsh[MAXN],HSH[MAXN],pw[MAXN],ok[MAXN],ans;
25 char ch[MAXN];
26 int check(int pos,int x)
27 {
28     return ((hsh[pos+x]-hsh[pos-x]*pw[2*x]%MOD+MOD)%MOD+(HSH[pos-x+1]-HSH[pos+x+1]*pw[2*x]%MOD+MOD)%MOD)%MOD==ok[2*x];
29 }
30 int main()
31 {
32     n=read();scanf("%s",ch+1);
33     for(int i=pw[0]=1;i<=n;i++) pw[i]=pw[i-1]*bas%MOD,hsh[i]=(ch[i]-'0'+hsh[i-1]*bas%MOD)%MOD,ok[i]=(1+ok[i-1]*bas%MOD)%MOD;
34     for(int i=n;i;i--) HSH[i]=(ch[i]-'0'+HSH[i+1]*bas%MOD)%MOD;
35     for(int i=1,l,r,mid,res;i<n;i++)
36     {
37         res=l=0,r=min(i,n-i);
38         while(l<=r)
39         {
40             mid=(l+r)>>1;
41             if(check(i,mid)) res=mid,l=mid+1;
42             else r=mid-1;
43         }
44         ans+=res;
45     }
46     printf("%lld",ans);
47 }
View Code

 

手写hash_table之类的还是算了吧 map就完事了

posted @ 2018-07-17 11:16  jack_yyc  阅读(178)  评论(0编辑  收藏  举报