字符串哈希题目总结

HDU1880

给出对应的字符串,需要建立相应的双向映射。也就是输入first串可以得出对应的second串,输入second串可以得出对应的first串

数据有100000 ,用map<string,string>模拟爆内存。

使用 BKDRHash 哈希函数进行哈希

// BKDR Hash Function
unsigned int BKDRHash(char *str)
{
    unsigned int seed = 131; // 31 131 1313 13131 131313 etc..
    unsigned int hash = 0;
 
    while (*str)
    {
        hash = hash * seed + (*str++);
    }
 
    return (hash & 0x7FFFFFFF);
}
代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 100010;
const int H = 100007;
struct node{
    char que[25];
    char ans[85];
    int next;
};
node nodeA[N], nodeB[N];
int curA,curB;
int hashTableA[N] , hashTableB[N];
void initHash(){
    for(int i=0;i<H;i++){
        hashTableA[i] = -1;
        hashTableB[i] = -1;
    }
    curA = 0;
    curB = 0;
}
unsigned int getHash(char *s){
    unsigned int seed = 131;
    unsigned int hash = 0;
    while(*s){
        hash = hash * seed + *s++;
    }
    return (hash & 0x7fffffff) % H;
}
void insertHash(char *que,char *ans){
    unsigned int h;
    h = getHash(que);
    strcpy(nodeA[curA].que, que);
    strcpy(nodeA[curA].ans, ans);
    nodeA[curA].next = hashTableA[h];
    hashTableA[h] = curA;
    ++curA;

    h = getHash(ans);
    strcpy(nodeB[curB].que,que);
    strcpy(nodeB[curB].ans,ans);
    nodeB[curB].next = hashTableB[h];
    hashTableB[h] = curB;
    ++curB;
}
int searchHashA(char *que){
    unsigned int h = getHash(que);
    int next = hashTableA[h];
    while(next != -1){
        if(strcmp(que,nodeA[next].que) == 0) return next;
        next = nodeA[next].next;
    }
    return -1;
}
int searchHashB(char *ans){
    unsigned int h = getHash(ans);
    int next = hashTableB[h];
    while(next != -1){
        if(strcmp(ans, nodeB[next].ans) == 0) return next;
        next = nodeB[next].next;
    }
    return -1;
}
int main(){
    char str[120], que[25], ans[85],qlen ,alen;
    int n,res;
    initHash();
    while(gets(str)){
        if(str[0] == '@') break;
        qlen = 0;
        for(int i=1;str[i] != ']';++i){
            que[qlen++] = str[i];
        }
        que[qlen] = 0;
        alen = 0;
        for(int i=qlen+3; str[i] != 0; ++i){
            ans[alen++] = str[i];
        }
        ans[alen] = 0;
        insertHash(que,ans);
    }
    scanf("%d",&n);
    getchar();
    while(n--){
        gets(str);
        if(str[0] == '['){
            qlen = strlen(str);
            str[qlen - 1] = 0;
            res = searchHashA(str + 1);
            if(res == -1) printf("what?\n");
            else printf("%s\n",nodeA[res].ans);
        }else{
            alen = strlen(str);
            res = searchHashB(str);
            if(res == -1) printf("what?\n");
            else printf("%s\n",nodeB[res].que);
        }
    }
    return 0;
}


POJ1743

意:有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复的主题。“主题”是整个音符序列的一个子串,它需要满足如下条件:

    1.长度至少为5个音符。

    2.在乐曲中重复出现。(可能经过转调,“转调”的意思是主题序列中每个音符都被加上或减去了同一个整数值)

    3.重复出现的同一主题不能有公共部分。

重复2次以上,不能有公共部分的最长子串。

后缀数组可解,

这里用到88进制取模进行哈希函数

想出这种方法的作者连接 http://blog.renren.com/share/283538506/12293487302/0


#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cstdlib>

using namespace std;
const int maxh = 10007;
const int maxn  =  20010;
typedef long long ll;
typedef unsigned long long ull;
#define mem(a,x) memset(a,x,sizeof a)
struct HASHMAP{
    int head[maxh] , next[maxn] , size;
    ull  state[maxn];
    int f[maxn];
    void init(){
        size = 0;
        mem(head,-1);
    }
    int insert(ull val,int id){
        int h = val % maxh;
        for(int i=head[h]; i != -1; i = next[i]){
            if(val == state[i]) return f[i];
        }
        f[size] = id;
        state[size] = val;
        next[size] = head[h];
        head[h] = size++;
        return f[size  - 1];
    }
};
HASHMAP H;
const int SEED = 13331;
ull P[maxn];
ull S[maxn];
int A[maxn];
int n;
bool check(int x){
    H.init();
    for(int i=x;i<n;i++)
        if(H.insert(S[i] - S[i-x]*P[x],i ) < i - x)
            return true;
    return false;
}
int main(){
    P[0] = 1;
    for(int i=1;i<maxn;i++)
        P[i] = P[i-1] * SEED; // unsigned long long 自动取模
    while(scanf("%d",&n) && n){
        for(int i=1;i<=n;i++)
            scanf("%d",&A[i]);
        for(int i=1;i<n;i++)
            A[i] = A[i + 1] - A[i];
        S[0] = 0;
        for(int i=1;i<n;i++)
            S[i] = S[i-1] * SEED + A[i];
        int ans = 0;
        int l = 4, r = n - 1;
        while(l <= r){
            int mid = (l + r) >>1;
            if(check(mid)){
                ans = mid;
                l = mid +1;
            }else
                r = mid -1;
        }
        if(ans < 4) ans = -1;
        ans++;
        printf("%d\n",ans);
    }
    return 0;
}






SCU4438 字符串hash ,好题

通过构造hash表,记录模式串的hash值,然后对主串按顺序遍历每个字符,并存入数组stack,并算出当前串的hash值,通过hash[i] - hash[i - len]*hashpow 算出当前子串是否与模式串hash值相等,相等就将数组stack中的下标回退len长度,实现删除匹配的串。

细节还有待推敲

代码

#include<cstdio>
#include<algorithm>
#include<map>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<cstdlib>
#include<iostream>
using namespace std;
#define maxn 5000006
typedef unsigned long long ll;
ll hash = 100007;
ll hash_w;
ll hash_p [maxn];
ll hash_pow[maxn];

char p[maxn], w[maxn];
char stack[maxn];
int len ;
void init(){
    hash_pow[0] = 1;
    for(int i=1;i<maxn;i++)
        hash_pow[i] = hash_pow[i -1] *hash;
}
bool check(int pos){

    if(pos >= len && hash_p[pos] -
        hash_p[pos - len] *hash_pow[len] == hash_w ){

       // cout<<pos<<" "<<hash_p[pos] <<" "<<hash_p[pos - len] *hash_pow[len]<<endl;
        return true;
    }

    return false;
}
int main(){
    init();
    while(~scanf("%s%s",w,p)){
        len = strlen(w);
        //模式串的hash值
        hash_w = 0;
        for(int i=0;*(w + i);i++){
            hash_w = hash_w * hash + *(w + i);
           // cout<<hash_w<<"  xx  ";
        }
      //  cout<<endl;

        int top = 0;
        for(int i=0; *(p + i); i++){
            stack[top++] = *(p + i);
            hash_p[top] = hash_p[top - 1]*hash + *(p + i);
            if(check(top)) top -= len;
        }
        for(int i=0;i<top;i++)
            printf("%c",stack[i]);
        puts("");
    }
    return 0;
}


HDU1280整数hash 水题

#include<cstdio>
#include<algorithm>
#include<map>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<cstdlib>
#include<iostream>
using namespace std;
#define maxn 5000006
typedef unsigned long long ll;
#define mem(a,x) memset(a,x,sizeof a)
int arr[3333];
int vis[10010];
int main(){
    int n,m;
    while(scanf("%d%d",&n,&m) != EOF){
        mem(vis,0);
        for(int i=0;i<n;i++){
            scanf("%d",&arr[i]);
        }
        for(int i=0;i<n;i++){
            for(int j=i+1;j<n;j++){
                vis[arr[i] + arr[j] ]  ++;
            }
        }
        bool isfirst = true;
        for(int i=10000;i>=0;i--){
                if(!m)break;
                while(m && vis[i]){
                    if(isfirst)
                        printf("%d",i);
                    else
                        printf(" %d",i);
                    isfirst = false;
                    m--;
                    vis[i]--;
                }


        }
        puts("");

    }
    return 0;
}

HDU1496

a*x1^2+b*x2^2+c*x3^2+d*x4^2=0

给出这样一个式子,给出a,b,c,d.求有多少解, abcd范围 [-50,50] x1 - x4 [-100,100];

hash,四层循环改成二层循环,通过数组映射判断是否冲突,冲突就说明匹配,说明有解。另外特殊情况该优化的还要优化,否者会超时。

#include<cstdio>
#include<algorithm>
#include<map>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<cstdlib>
#include<iostream>
using namespace std;
typedef unsigned long long ll;
#define mem(a,x) memset(a,x,sizeof a)
//-50*(10000) *2 = 1e 6;
const int maxn = 1e6 + 2;
int vis[2000010]; //2e6 + 10;
int main(){
    int a,b,c,d;

    while(scanf("%d%d%d%d", &a,&b,&c,&d) != EOF){
        if(a > 0 && b> 0 && c > 0 && d > 0){//没有这句就超市了
            puts("0");
            continue;
        }
        mem(vis,0);
        for(int i=1;i<=100;i++){
            for(int j=1;j<=100;j++){
                vis[a*i*i + b*j*j + maxn ] ++;
            }
        }
        int ans = 0;
        for(int i=1;i<=100;i++){
            for(int j=1;j<=100;j++){
                ans += vis[-(c*i*i + d*j*j)+maxn];
            }
        }
        // 每个i ,j都是可正 可负,2 * 2 * 2 * 2 == 16
        printf("%d\n",ans*16);
    }
    return 0;
}

ACDREAM 1726

给出N个数 和一个数 H ,从N个数中选择任意多个数,问有多少种选择使得选的数的和 等于H 。

N <=40 H <= 1e9

1e9需要哈希一下。

N = 40 折半之后哈希就可以了。

二分数据,数据减半后,时间复杂度 有 2 ^ 40 变为 2 ^ 20;

再用HASH判断是否满足

#include<cstdio>
#include<algorithm>
#include<map>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<cstdlib>
#include<iostream>
using namespace std;
typedef long long ll;
#define mem(a,x) memset(a,x,sizeof a)
const int maxn = 1<<20; // 1048576
const int hash = 1000007; //开多大的hash

/*
3 1000010
next记录的是之前的head
head代表着数据在a数组中的位置。
head始终代表着头结点

*/
struct hashmap{
    ll a[maxn]; //映射入的数组
    int head[hash] , next[maxn],size;
    void init(){
        mem(head,-1);
        size = 0;
    }
    /*
    取模相同的数,存入a中之后,可以通过next遍历找到head进而找到对应a[i];
    实现了取模相同的数放到邻接表里一样的查询。
    */
    bool find(ll val){ //查找一个元素是否在hash表中
        int tmp = (val % hash + hash) % hash;
        for(int i=head[tmp]; i != -1;i = next[i]){
            if(val == a[i]) return true;
        }
        return false;
    }
    void add(ll val){ // 添加元素到hash表
        int tmp = (val % hash + hash) %hash;
        if(find(val)) return ;
        a[size] = val;
        next[size] = head[tmp];
        head[tmp] = size ++;
    }
}h1;
int n,m,num[55];

int main(){
//
//    h1.init();
//    h1.add(3);
//    h1.add(4);
//    h1.add(5);
//    h1.add(6);
//    h1.add(1000010);
//    h1.add(2000017);

    while(~scanf("%d%d",&n,&m)){
        h1.init();
        for(int i=0;i<n;i++) scanf("%d",num+i);
        int t = n/2;
        // 将t种取或者不取 映射为二进制数,进行枚举
        for(int i=0;i<(1<<t);i++){
            ll sum = 0;
            for(int j=0;j < t;j++){
                if(i & (1<<j)){
                    sum += num[j]; //加上选中的数
                }
            }
            if(sum > m) continue;
            h1.add(sum);
        }
        int tt = n - t; //枚举剩下的数
        int flag = 0;
        for(int i=0;i<(1<<tt);i++){
            ll sum = 0;
            for(int j=0;j<tt;j++){
                if(i & (1<<j))
                    sum += num[t + j]; //加上剩下的数中符合条件的
            }
            if(sum > m) continue;
            if(h1.find(m - sum)){
                flag = 1; break;
            }
        }
        if(flag)
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;
}


  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值