HASH刷题整理
- 简单整理了下自己学习hash时做的题
[poj3461]Oulipo
题目描述
这是一道模板题。
给定一个字符串A和一个字符串B,求B在A中的出现次数。A和B中的字符均为英语大写字母或小写字母。
A中不同位置出现的B可重叠。
输入格式
输入共两行,分别是字符串A和字符串B。
输出格式
输出一个整数,表示B在A中的出现次数。
样例
样例输入
3
BAPC
BAPC
AZA
AZAZAZA
VERDI
AVERDXIVYERDIAN
样例输出
1
3
0
数据范围与提示
\(1≤A,B 的长度 \leq 10 ^ 6\) 仅包含大小写字母。
Solution
模板题……
#include
#include
#include
#define MAXN 1000005
#define base 107
char a[MAXN],b[MAXN];
unsigned long long h[MAXN];
unsigned long long power[MAXN];
unsigned long long counta;
int len_a;
int len_b;
int T;
int main(){
power[0] = 1;
for(register int i=1;i
[poj2406]Power Strings
题目描述
给定若干个长度\(\leq 10^6\)的字符串,询问每个字符串最多是由多少个相同的子字符串重复连接而成的。如:ababab
则最多有 3个 ab
连接而成。
输入格式
输入若干行,每行有一个字符串,字符串仅含英语字母。特别的,字符串可能为 .
即一个半角句号,此时输入结束。
样例
样例输入
abcd
aaaa
ababab
.
样例输出
1
4
3
数据范围与提示
字符串长度 \(\leq 10^6\)
Solution
- 先预处理出HASH值,然后枚举长度,然后验证一遍即可。
- 或者不用HASH做,直接枚举长度进行验证。
#include
#include
#include
#define base 1e4+7
#define MAXN 1000005
char s[MAXN];
inline bool C(int len,int size){
for(register int i=0;i
[poj2752]Seek the Name, Seek the Fame
题目描述
给定若干字符串(这些字符串总长 \(\leq 4 \times 10^5\)),在每个字符串中求出所有既是前缀又是后缀的子串长度。
例如:ababcababababcabab
,既是前缀又是后缀的:ab
,abab
,ababcabab
,ababcababababcabab
。
输入格式
输入若干行,每行一个字符串。
输出格式
对于每个字符串,输出一行,包含若干个递增的整数,表示所有既是前缀又是后缀的子串长度。
样例
样例输入
ababcababababcabab
aaaaa
样例输出
2 4 9 18
1 2 3 4 5
Solution
正着HASH一遍,反正HASH一遍,然后枚举长度,比较是否相等并计数即可。
#include
#include
#define MAXN 400005
#define base (unsigned long long)(1e5+7)
char s[MAXN];
unsigned long long power[MAXN];
unsigned long long C[MAXN];
int main(){
power[0] = 1;
for(register int i=1;i=0?i-1:0]*base + s[i] - 'a' + 1;
}
for(register int i=1;i<=len;++i){
unsigned long long a = C[i-1];
unsigned long long b = C[len-1] - (len-i-1>=0?C[len-i-1]:0)*power[i];
if(a==b)printf("%d ",i);
}
puts("");
}
return 0;
}
[bzoj3916]Friends
题目描述
原题来自:BalticOI 2014
有三个好朋友喜欢在一起玩游戏,A 君写下一个字符串 S,B 君将其复制一遍得到T,C 君在 T 的任意位置(包括首尾)插入一个字符得到 U。现在你得到了 U,请你找出S。
输入格式
第一行一个数 N,表示U 的长度。 第二行一个字符串U,保证U 由大写字母组成。
输出格式
输出一行,若 S不存在,输出 NOT POSSIBLE
。若S不唯一,输出 NOT UNIQUE
,否则输出 S。
样例
样例输入 1
7
ABXCABC
样例输出 1
ABC
样例输入 2
6
ABCDEF
样例输出 2
NOT POSSIBLE
样例输入 3
9
ABABABABA
样例输出 1
NOT UNIQUE
数据范围与提示
\(2 \leq N \leq 2000001\)
Solution
枚举插入的字符,然后截断比较一下,进行计数即可,这里要特别注意若在不同的地方插入字符,最后截出来的字符串如果是一样的,则算同一个解。
#include
#include
#include
#define base (unsigned long long)(1e5+7)
#define MAXN 2000005
char s[MAXN];
unsigned long long power[MAXN];
unsigned long long C[MAXN];
unsigned long long lastans = 0;
int N;
int main(){
scanf("%d\n",&N);
scanf("%s",s+1);
power[0] = 1;
for(register int i=1;i> 1;
C[0] = 0;
for(register int i=1;i<=N;++i){
C[i] = C[i-1]*base + s[i] - 'A' + 1;
}
int ans = 0;
int count = 0;
unsigned long long a,b;
for(register int i=1;i<=N;++i){
if(i==mid){
a = C[mid-1];
b = C[N] - C[mid]*power[N-mid];
}
else if(i1){
if(a==lastans)count--;
else break;
}
}
}
if(count==0){
puts("NOT POSSIBLE");
return 0;
}
if(count>1){
puts("NOT UNIQUE");
return 0;
}
count = 0;
for(register int i=1;i<=N;++i){
if(i==ans)continue;
putchar(s[i]);
if((++count)==mid-1)break;
}
return 0;
}
[luogu3498] POI2010 KOR-Beads
题目描述
Byteasar 决定制造一条项链,她买了一串珠子,她有一个机器,能把这条珠子切成很多段,使得每段恰有 k个珠子 (k>0),如果这条珠子的长度不是k的倍数,最后一块长度小于k的段就被丢弃了。
Byteasar 想知道,选择什么数字k 可以得到最多的不同的段。注意这里的段是可以反转的,即,子串 1,2,3 和 3,2,1 被认为是一样的。
输入格式
第一行一个正整数n,表示珠子的长度。
第二行 n个空格隔开的正整数 \(a_1,a_2,⋯a_n\) ,描述这一串珠子的颜色。
输出格式
第一行两个空格隔开的正整数,第一个表示能获得的最大不同的段的个数,第二个表示能获得最大值的k 的个数。
第二行若干空格隔开的正整数k,表示所有能够取得最大值的k ,请将k按照从小到大的顺序输出。
样例
输入样例
21
1 1 1 2 2 2 3 3 3 1 2 3 3 1 2 2 1 3 3 2 1
输出样例
6 1
2
数据范围与提示
\(1 \leq n \leq 200000,1 \leq a_i \leq n\)
Solution
先预处理正反的HASH,然后枚举长度,开始计算个数即可。
乍一看是超时的\(O(N^2)\)算法,但是考虑到每次里层的循环都是\(N/i\)次
便有 $1 + \frac{1}{2} + \frac{1}{3} + ... + \frac{1}{N} $,是远远到不了 \(O(N^2)\) 的。
#include
#include
#define MAXN 200005
#define base (unsigned long long)10007
#define MOD 1000007
unsigned long long power[MAXN];
unsigned long long Pre[MAXN];
unsigned long long Suf[MAXN];
int a[MAXN];
int ans[MAXN];
struct Node{
int vis;
unsigned long long hash;
}H[MOD];
int N,tot=0;
inline void add(unsigned long long x,int len){
int hash = (int)((x%MOD+MOD)%MOD);
while(H[hash].vis==len){
if(H[hash].hash==x)return;
hash = hash+1==MOD ? 0 : hash+1;
}
H[hash].vis = len;
H[hash].hash = x;
}
inline bool live(unsigned long long x,int len){
int hash = (int)((x%MOD+MOD)%MOD);
while(H[hash].vis==len){
if(H[hash].hash==x)return true;
hash = hash+1==MOD ? 0 : hash+1;
}
return false;
}
int main(){
scanf("%d",&N);
for(register int i=1;i<=N;++i){
scanf("%d",&a[i]);
}
std::memset(H,0,sizeof(H));
Pre[0] = 0;
for(register int i=1;i<=N;++i){
Pre[i] = Pre[i-1]*base + (unsigned long long)a[i];
}
Suf[N+1] = 0;
for(register int i=N;i>0;--i){
Suf[i] = Suf[i+1]*base + (unsigned long long)a[i];
}
power[0] = 1;
for(register int i=1;i<=N;++i){
power[i] = power[i-1]*base;
}
int maxx = 0;
for(register int len=1;len<=N;++len){
int count = 0;
for(register int i=1;i+len-1<=N;i+=len){
unsigned long long front = Pre[i+len-1] - Pre[i-1]*power[len];
unsigned long long back = Suf[i] - Suf[i+len]*power[len];
if(live(front,len)||live(back,len))continue;
count++;
add(front,len);add(back,len);
}
if(count>maxx){
maxx = count;
tot = 1;
ans[1] = len;
}
else if(count==maxx){
ans[++tot] = len;
}
}
printf("%d %d\n",maxx,tot);
for(register int i=1;i<=tot;++i)printf("%d ",ans[i]);
return 0;
}
[luogu3501] POI2010 ANT-Antisymmetry
题目描述
对于一个01字符串,如果将这个字符串0和1取反后,再将整个串反过来和原串一样,就称作“反对称”字符串。比如00001111和010101就是反对称的,1001就不是。
现在给出一个长度为N的01字符串,求它有多少个子串是反对称的。
输入格式
第一行一个正整数n 。
第二行一个长度为 n的 0/1字符串。
输出格式
一行一个整数,表示原串的反对称子串个数。
样例
样例输入
8
11001011
样例输出
7
数据范围与提示
\(1 \leq n \leq 500 000\) 。
Solution
这题真的好玩,我是没想出来
对于一个反对称的01串,则包含在其中的01串也是反对称的,前提是两个串的中点一致,所以可以枚举中点,进行二分查找最远的可到达的点即可。
#include
#include
#include
#define MAXN 500005
#define base (unsigned long long)1000007
unsigned long long Pre[MAXN];
unsigned long long Suf[MAXN];
unsigned long long power[MAXN];
char s[MAXN];
int N,tot;
inline bool Check(int l1,int r1,int l2,int r2){
if(l1<0||r2>N+1)return false;
unsigned long long a = Pre[r1] - Pre[l1-1]*power[r1-l1+1];
unsigned long long b = Suf[l2] - Suf[r2+1]*power[r1-l1+1];
return a==b;
}
int main(){
scanf("%d\n",&N);
scanf("%s",s+1);
power[0] = 1;
for(register int i=1;i<=N;++i){
power[i] = power[i-1]*base;
}
Pre[0] = 0;
for(register int i=1;i<=N;++i){
Pre[i] = Pre[i-1]*base + s[i] - '0' + 1;
}
Suf[N+1] = 0;
for(register int i=N;i>0;--i){
Suf[i] = Suf[i+1]*base + ((s[i]-'0')^1) + 1;
}
long long ans = 0;
for(register int i=1;i>1);
while(l>1;
if(Check(i-mid+1,i,i+1,i+mid))l = mid;
else r = mid-1;
}
ans += r;
}
printf("%lld",ans);
return 0;
}