HDU4821-String

HDU4821-String

题意

给一个字符串\(s\),问满足下列两个条件的子串有多少个。
1、子串的长度为\(N*M\)(表示其由\(M\)个长度为\(N\)的字符串拼成)
2、这\(M\)个长度为\(N\)的字符串必须两两互不相同(两个字符串任意一个位置上的字符不相同即可)

题解

每个字符串的长度给定为\(L\),想到\(Hash(l~r)=Hash[r]-Hash[l-1]*sum[N]\)。对于以\(i\)为起点的长为\(N*M\)的子串,把他的\(M\)个长度为\(N\)的小子串的哈希值均放入\(map\)中,如果\(map.size()==M\),那么说明他们互不相同。
如果枚举每个起点,会造成TLE。我们可以使用尺取法。对于已经找到的一个子串,把他的最前面的小子串去掉,在最后再加上一个长为\(N\)的小子串,不断循环,可以得到一系列的子串。那么可以将原串分成\(N\)个系列,从\(1\)\(N\)枚举起点,不断生成,就可以得到所有的子串。

1、Hash值开long long

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int base=131;
const int mod=1e9+7;
const int N=1e5+10;

int M,L;
char s[N];
ll Hash[N];
int len;
ll sum,ans;
map<ll,int> m;

//Hash开longlong 

void getHash(char a[]){
    for(int i=1;i<=len;i++){
        Hash[i]=(Hash[i-1]*base%mod+s[i])%mod;
    }
}

void init(){
    sum=1;
    for(int i=1;i<=L;i++) sum=(sum*base)%mod;
}

void solve(int st){
    m.clear();
    int e=st+M*L-1;
    if(e>len) return;
    int l=st,r=st+L-1;
    for(int i=1;i<=M;i++){
        ll now=(Hash[r]-Hash[l-1]*sum%mod+mod)%mod;
        m[now]++; 
        l+=L;r+=L;
    }
    if(m.size()==M) ans++;
    while(e+L<=len){
        e+=L;
        l=st;r=l+L-1;
        ll now=(Hash[r]-Hash[l-1]*sum%mod+mod)%mod;
        m[now]--;
        if(m[now]==0) m.erase(now);
        st+=L;
        
        r=e;l=r-L+1;
        now=(Hash[r]-Hash[l-1]*sum%mod+mod)%mod;
        m[now]++;
        if(m.size()==M) ans++;
    }
} 

int main(){
    while(scanf("%d%d",&M,&L)!=EOF){
        ans=0;
        scanf("%s",s+1);
        len=strlen(s+1);
        getHash(s);
        init();
        for(int i=1;i<=L;i++){
            solve(i);
        }
        printf("%lld\n",ans);
    }
    return 0;
} 

猜你喜欢

转载自www.cnblogs.com/qjy73/p/12498418.html