hdoj4821(字符串hash+map判重)

题目链接:https://vjudge.net/contest/362409#problem/I

题意:给定一个字符串s,求有多少子串,满足长度为M*L,且由M个不同的子串(长度均为L)组成。


思路:
  先用hs[i]记录前i个字符的hash值,然后利用hs[r]-hs[l-1]*base[r-l+1]得到子串[l,r]的hash值。(自然溢出可AC,模1e18的质数过不了)

  利用上面的方法可以O(1)得到子串[l,r]的hash值。然后就是遍历,以i(1<=i<=L)为起点,将i后面的M个长度为L的子串放进map里判重,然后通过删除第一个,添加后面一个来移动。删除时如果map值为0,要将其从map中删除。复杂度为O(L*n/L)=O(n)。

AC code:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<tr1/unordered_map>
using namespace std;
using namespace tr1;

typedef unsigned long long ull;
const int maxn=1e5+5;
int n,M,L,ans;
char s[maxn];
ull base=131,bs[maxn],hs[maxn];
unordered_map<ull,int> mp;

ull gethash(int l,int r){
    return (hs[r]-hs[l-1]*bs[r-l+1]);
}

void init(){
    bs[0]=1;
    for(int i=1;i<maxn;++i)
        bs[i]=bs[i-1]*base;
}

int main(){
    init();
    while(~scanf("%d%d",&M,&L)){
        scanf("%s",s+1);
        n=strlen(s+1);
        ans=0;
        hs[0]=0;
        for(int i=1;i<=n;++i)
            hs[i]=(hs[i-1]*base+s[i]);
        for(int i=1;i<=L;++i){
            mp.clear();
            int p=i,num=0;
            while(p+L<=n+1){
                ull t1=gethash(p,p+L-1);
                ++mp[t1];
                ++num;
                if(num==M) break;
                p+=L;
            }
            if(num!=M) break;
            if(mp.size()==M) ++ans;
            p+=L;
            while(p+L<=n+1){
                ull t2=gethash(p-M*L,p-M*L+L-1);
                ull t3=gethash(p,p+L-1);
                ++mp[t3];
                --mp[t2];
                if(mp[t2]==0) mp.erase(t2);    
                if(mp.size()==M) ++ans;
                p+=L;
            }
        }
        printf("%d
",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/FrankChen831X/p/12502321.html