HDU 4821 String 字符串hash map的妙用

来源:互联网 发布:world of goo mac 编辑:程序博客网 时间:2024/05/17 12:18

题意:给出一个字符串。求有多少个子串,长度为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的字符串为左闭右开区间。切记,切记。

0 0
原创粉丝点击