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

HASH刷题整理

时间:2018-08-05 13:01:52      阅读:192      评论:0      收藏:0      [点我收藏+]

标签:end   continue   次数   mem   ant   \n   try   二分查找   count   

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 <cstdio>
#include <cstring>
#include <cmath>
#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<MAXN;++i)power[i] = power[i-1]*base;

    scanf("%d",&T);
    for(register int k=1;k<=T;++k){
        
        scanf("%s%s",a,b);
        
        counta = 0;
        len_a = std::strlen(a);
        len_b = std::strlen(b);

        for(register int i=0;i<len_a;++i)
            counta = counta*base + (a[i]-'A'+1);

        h[0] = b[0]-'A'+1;
        for(register int i=1;i<len_b;++i)
            h[i] = h[i-1]*base + b[i] - 'A' + 1;

        int ans = 0;
        if(counta==h[len_a-1])ans++;

        for(register int i=0;i+len_a<len_b;++i){
            if(counta==h[i+len_a] - h[i]*power[len_a])ans++;
        }

        printf("%d\n",ans);
    }
    return 0;
}

[poj2406]Power Strings

题目描述

给定若干个长度\(\leq 10^6\)的字符串,询问每个字符串最多是由多少个相同的子字符串重复连接而成的。如:ababab 则最多有 3个 ab 连接而成。

输入格式

输入若干行,每行有一个字符串,字符串仅含英语字母。特别的,字符串可能为 . 即一个半角句号,此时输入结束。

样例

样例输入

abcd
aaaa
ababab
.

样例输出

1
4
3

数据范围与提示

字符串长度 \(\leq 10^6\)

Solution

  1. 先预处理出HASH值,然后枚举长度,然后验证一遍即可。
  2. 或者不用HASH做,直接枚举长度进行验证。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define base 1e4+7
#define MAXN 1000005

char s[MAXN];
inline bool C(int len,int size){
    for(register int i=0;i<len;++i){
        for(register int j=i;j<size;j+=len)
            if(s[j]!=s[i])return false;
    }
    return true;
}
int main(){

    while(true){
        
        scanf("%s",s);
        if(s[0]=='.')break;

        int len = std::strlen(s);
        int ans = 1;
        for(register int i=1;i<len;++i){
            if(len%i!=0)continue;
            if(C(i,len)){
                ans = len / i;
                break;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

[poj2752]Seek the Name, Seek the Fame

题目描述

给定若干字符串(这些字符串总长 \(\leq 4 \times 10^5\)),在每个字符串中求出所有既是前缀又是后缀的子串长度。

例如:ababcababababcabab,既是前缀又是后缀的:abababababcababababcababababcabab

输入格式

输入若干行,每行一个字符串。

输出格式

对于每个字符串,输出一行,包含若干个递增的整数,表示所有既是前缀又是后缀的子串长度。

样例

样例输入

ababcababababcabab
aaaaa

样例输出

2 4 9 18
1 2 3 4 5

Solution

正着HASH一遍,反正HASH一遍,然后枚举长度,比较是否相等并计数即可。

#include <cstdio>
#include <cstring>
#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<MAXN;++i){
        power[i] = power[i-1]*base;
    }

    while(scanf("%s",s)!=EOF){
        
        int len = std::strlen(s);

        C[0] = 0;
        for(register int i=0;i<len;++i){
            C[i] = C[i-1>=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 <cstdio>
#include <cstring>
#include <algorithm>
#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<MAXN;++i){
        power[i] = power[i-1]*base;
    }
 
    if(N&1==0||N<=1){
        puts("NOT POSSIBLE");
        return 0;
    }
 
    int mid = (N+1) >> 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(i<mid){
            a = C[i-1]*power[(mid-1)-(i-1)] + C[mid] - C[i]*power[mid-i];
            b = C[N] - C[mid]*power[N-mid];
        }
        else{
            a = C[mid-1];
            b = (C[i-1] - C[mid-1]*power[(i-1)-(mid-1)])*power[(mid-1)-(i-mid)] + (C[N] - C[i]*power[N-i]);
        }
        if(a==b){
            ans = i;
            count++;
            if(count==1)lastans = a;
            if(count>1){
                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 <cstdio>
#include <cstring>
#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 <cstdio>
#include <cstring>
#include <algorithm>
#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<N;++i){
        int l = 0;
        int r = (N>>1);
        while(l<r){
            int mid = (l+r+1)>>1;
            if(Check(i-mid+1,i,i+1,i+mid))l = mid;
            else r = mid-1;
        }
        ans += r;
    }
    printf("%lld",ans);
    return 0;
}

HASH刷题整理

标签:end   continue   次数   mem   ant   \n   try   二分查找   count   

原文地址:https://www.cnblogs.com/Neworld2002/p/9424968.html

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