POJ 2774 HDU 4821 -- Hash算法

Hash的用处就是将一个字符串从头到尾的特殊化记录一下,每次要判断字符串是否相等,直接看Hash值是否相等就好了
这里用的均为BKDR-Hash

POJ 2774
题目链接:http://poj.org/problem?id=2774
题目大意:给你两个字符串,求它们的最长公共子串的长度
思路:对两个字符串都Hash扫一下, 通过二分来判定公共子串长度,先求s1中长度为t的子串的Hash值,再求s2中的,对比是否相等
AC代码:

#include<bits/stdc++.h>
using namespace std;

typedef unsigned long long ull;
const int maxn = 100005;

char s1[maxn], s2[maxn];
ull hash1[maxn], hash2[maxn], a[maxn], pows[maxn];
int len1, len2;

inline ull getHash(ull h[], int i, int j){      //计算[i, j)子串的hash值
    return h[j] - h[i] * pows[j-i];
}

bool solve(int t)       //判断是否有长度为t的解
{
    int n = 0;
    for(int i = t; i <= len1; i++){
        a[n++] = getHash(hash1, i-t, i);    //计算s1的每一个长度为t的子串的hash值
    }
    sort(a, a+n);
    for(int i = t; i <= len2; i++){
        ull h = getHash(hash2, i-t, i);     //计算s2的每一个长度为t的子串的hash值
        if(binary_search(a, a+n, h))    return true;    //查找s1中是否有子串hash值与其相等
    }
    return false;
}
int main()
{
    cin >> s1 >> s2;
    len1 = strlen(s1), len2 = strlen(s2);
    int l = 0, r = max(len1, len2);
    pows[0] = 1;
    for(int i = 1; i <= r; i++){
        pows[i] = pows[i-1]*131;      //预处理幂次
    }
    hash1[0] = 0;
    for(int i = 0; i < len1; i++){
        hash1[i+1] = hash1[i]*131 + s1[i];  //  预处理s1的前缀hash值
    }
    for(int i = 0; i < len2; i++){
        hash2[i+1] = hash2[i]*131 + s2[i];
    }
    while(l != r){
        int t = (l+r) >> 1;
        if(solve(t+1))  l = t+1;
        else    r = t;
    }
    cout << l << '\n';
    return 0;
}

HDU 4821
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4821
题目大意:一个字符串,它的子串有m个长为l的小子串构成,这些小子串各不相同,问有多少个子串?
思路:Hash之后,从头扫+移块扫,对每个长为m*l的子串内部进行判断,可以用map来进行存储每一个小子串的Hash值,方便计数
AC代码:

#include<bits/stdc++.h>
using namespace std;

typedef unsigned long long ull;
const int maxn = 1e5+5;

char s[maxn];
ull Hash[maxn], base[maxn];
map<ull, int> p;

int main()
{
    int m, l;
    while(~scanf("%d %d", &m, &l)){
        cin >> s;
        int len = strlen(s);
        base[0] = 1;
        for(int i = 1; i <= len; i++){
            base[i] = base[i-1]*131;
        }
        Hash[0] = 0;
        for(int i = 0; i < len; i++){
            Hash[i+1] = Hash[i]*131 + s[i];
        }
        int a = 0;
        for(int i = 0;  i < l && i+m*l <= len; i++){
            p.clear();
            for(int j = i; j < m*l+i; j+=l){
                p[Hash[j+l] - Hash[j]*base[l]]++;
            }
            if(p.size() == m)   a++;
            for(int j = i; j+l <= len-m*l; j+=l){       //移块扫
                p[Hash[j+l] - Hash[j]*base[l]]--;
                if(p[Hash[j+l] - Hash[j]*base[l]] == 0)   p.erase(Hash[j+l] - Hash[j]*base[l]); //  将原先块区删除
                p[Hash[j+m*l+l] - Hash[j+m*l]*base[l]]++;       //  添加新的块区
                if(p.size() == m)   a++;
            }
        }
        cout << a << '\n';
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值