HDU 4821 String 字符串hash map的妙用

题意:给出一个字符串。求有多少个子串,长度为M*L,从前到后,分成M个长度为L的子串,每个子串都不相同。

思路:字符串hash。

           因为起始位置有L种可能,我们要分别统计。利用hash后的值,我们可以判断子串是否重复。

           首先,我先想到的是记录每个子串在L中情况下,离他最近的且相同的子串的位置,如果位置大于M*L,那就可以继续扩展一个长度为L的子串,当长度达到M*L的时候,我们就可以答案加1.

代码如下:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>

using namespace std;

const int MAGIC = 233;
const int MAX = 100010;
unsigned int h[MAX];
unsigned int base[MAX];
int cnt[MAX];
map<unsigned int,int> ma[MAX];
char s[MAX];
int M,L;

inline void init_hash(char *s, unsigned int *h, int l)
{
    h[0]= 0;
    for(int i = 1; i <= l; ++i)
        h[i] = h[i-1] * MAGIC + s[i-1];
    base[0] = 1;
    for(int i = 1; i <= l; ++i)
        base[i] = base[i-1] * MAGIC;
}

inline unsigned int string_hash(unsigned *h, int l, int r)
{
    return h[r] - h[l]* base[r-l];
}

int main(void)
{
    //freopen("input.txt","r",stdin);
    while(scanf("%d %d", &M, &L) != EOF)
    {
        scanf("%s",s);
        int n = strlen(s);
        int ans = 0;
        init_hash(s,h,n);
        memset(cnt,0,sizeof(cnt));
        for(int i = 0; i < L; ++i)
            ma[i].clear();
        for(int i = 0; i < L; ++i)
            cnt[i] = 0;
        for(int i = L; i <= n; ++i){
            unsigned ha = string_hash(h,i-L,i);
            int id = i % L;
            if(!ma[id].count(ha))
            {
                cnt[i] = cnt[i-L] + L;
                if(cnt[i] >= M * L){
                    ans++;
                    cnt[i] = M * L;
                }
            }
            else
            {
                if((i - ma[id][ha] > cnt[i-L]))
                {
                    cnt[i] = cnt[i-L] + L;
                    if(cnt[i] >= M * L){
                        ans++;
                        cnt[i] = M * L;
                    }
                }
                else
                    cnt[i] = i - ma[id][ha];
            }
            ma[id][ha] = i;
        }
        printf("%d\n",ans);
    }
    return 0;
}

但是不用这么麻烦的。map既可以用来计数,也可用来判重。这样,我们直接利用每种情况下map的size的大小就行了。

代码如下:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>

using namespace std;

const int MAGIC = 233;
const int MAX = 100010;
unsigned int h[MAX];
unsigned int base[MAX];
map<unsigned int,int> ma[MAX];
char s[MAX];
int M,L;

inline void init_hash(char *s, unsigned int *h, int l)
{
    h[0]= 0;
    for(int i = 1; i <= l; ++i)
        h[i] = h[i-1] * MAGIC + s[i-1];
    base[0] = 1;
    for(int i = 1; i <= l; ++i)
        base[i] = base[i-1] * MAGIC;
}

inline unsigned int string_hash(unsigned *h, int l, int r)
{
    return h[r] - h[l]* base[r-l];
}

int main(void)
{
    //freopen("input.txt","r",stdin);
    while(scanf("%d %d", &M, &L) != EOF)
    {
        scanf("%s",s);
        int n = strlen(s);
        int ans = 0;
        init_hash(s,h,n);
        for(int i = 0; i < L; ++i)
            ma[i].clear();
        for(int i = L; i <= n; ++i){
            unsigned ha = string_hash(h,i-L,i);
            int id = i % L;
            ma[id][ha]++;
            if(ma[id].size() == M) ans++;
            if(i >= M * L){
                int st = i - M * L;
                unsigned ha1 = string_hash(h,st,st+L);
                if(--ma[id][ha1] == 0) ma[id].erase(ha1);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

 注意:也给自己提个醒吧,在上面的hash函数中,需要hash的字符串为左闭右开区间。切记,切记。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值