[SMOJ1813]不同子串个数

来源:互联网 发布:手机淘宝网试衣间在哪 编辑:程序博客网 时间:2024/06/05 02:49

题目描述

给定一个包含大小写字母的字符串和当中可能出现的不同字符数 NC,求该字符串中长度为 N 的不同子串个数。
例如,考虑 N=3,NC=4 和字符串“daababac”。在串中可以找到的大小为 3 的不同子字符串是:“daa”; “aab”; “aba” “bab” “bac”。
所以答案应该是 5。

输入格式 1813.in

第一行输入由两个数字 NNC 组成,分隔一个空格。之后是一行字符串,长度不超过1600万。

输出格式 1813.out

输出一个在给定字符串中找到的长度为 N 的不同子串个数。

输入样例 1813.in

3 4
daababac

输出样例 1813.out

5

提示

输入量较大,C++选手建议使用scanf。


考虑到数据范围是比较大的,假设我们要枚举每个子串,然后逐位编码,得到一个 hash 值,如果该值曾经出现过就不计数,否则答案加 1,并将该值标记。
这种做法的时间复杂度应该是 O((lenn+1)×n),显然是会 TLE 的,那么我们只能预先将整个串编码,然后在 O(1) 时间内算出属于一个子串的编码,才能在总共 O(n) 的时间内解决整个问题。

问题的关键在于一种将整个串编码,还能分解出子串编码的方式。这里介绍一种字符串编码的方法。

首先我们会想一下二进制数。
对于任意一个二进制数,我们将它化为 10 进制的数的方法如下(以二进制数 1101101 为例):

1101101=26+25+23+22+20

hash 用的也是一样的原理,为每一个前缀,Hashi=Hashi1×x+si(其中 1<in,Hash0=0)。
一般地,

Hashi=s1xi+s2xi1+s3xi2++si

而对于 [l,r] 区间的 hash 值,则为:

Hash(l,r)=HashrHashl1xrl+1

但是如果 n 很大呢?那样不是会溢出了吗?

因此我们把 hash 值每计算一次就对一个较大的质数 p 取模, 那样就不会溢出了。但要注意,因为取了模,所以在算某个子串的时候,减了之后可能为负数,要先统一加上 p,避免负数的出现。
还有,多次计算的时候,权 xi 其实可以预处理保存到数组里,减少不必要的重复计算。

因此我们可以通过 Hash 值来比较两个字符串是否相等。
给出字符串 hash 的处理:

char s[N];int hash[N];void init(){ //处理hash值    p[0] = 1;    hash[0] = 0;    int n = strlen(s + 1);   for (int i = 1; i <= 100000; i++) p[i] = (p[i - 1] * base) % Prime;   for (int i = 1; i <= n; i++) hash[i] = (hash[i - 1] * base + (s[i] - 'A')) % Prime;}int get(int l, int r){ //取出 [l, r] 里面的字符串的 hash 值    return (hash[r] - hash[l - 1] * p[r - l + 1] + Prime) % Prime;}

用这样的方法给本题中的字符串编码,再枚举每个子串的开头或结尾,通过 n 算出另一端,得到该子串的 hash 值,就可以判断和计算了。

此题的POJ(1200)数据很水,请勿轻信网上的所谓“AC”程序,大部分都是错的

以下代码来自gsh:

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>#include<vector>using namespace std;#define MOD 4933333LL#define Maxn 5000003char s[Maxn];bool hash[MOD];bool hash2[MOD];bool hash3[MOD];bool hash4[MOD];bool hash5[MOD];bool hash6[MOD];long long sum[Maxn];long long kum[Maxn];long long lum[Maxn];long long mum[Maxn];long long num[Maxn];long long oum[Maxn];int find(long long h){    if(hash[h]==true)return 0;    hash[h]=true;    return 1;}int find2(long long h){    if(hash2[h]==true)return 0;    hash2[h]=true;    return 1;}int find3(long long h){    if(hash3[h]==true)return 0;    hash3[h]=true;    return 1;}int find4(long long h){    if(hash4[h]==true)return 0;    hash4[h]=true;    return 1;}int find5(long long h){    if(hash5[h]==true)return 0;    hash5[h]=true;    return 1;}int find6(long long h){    if(hash6[h]==true)return 0;    hash6[h]=true;    return 1;}int n,nc;long long ans;int main(){    freopen("1813.in","r",stdin);    freopen("1813.out","w",stdout);    scanf("%d%d",&n,&nc) ;    ans = 0;    scanf("%s",&s);    int len=strlen(s);      if(len<n)    {        puts("0");        return 0;    }    long long p=1,pp=1,ppp=1,pppp=1,ppppp=1,pppppp=1;    for(int i=1;i<=n;i++)p=p*31LL%MOD;    for(int i=1;i<=n;i++)pp=pp*233LL%MOD;    for(int i=1;i<=n;i++)ppp=ppp*107LL%MOD;    for(int i=1;i<=n;i++)pppp=pppp*131LL%MOD;    for(int i=1;i<=n;i++)ppppp=ppppp*1313LL%MOD;    for(int i=1;i<=n;i++)pppppp=pppppp*1331LL%MOD;    sum[0]=s[0];kum[0]=s[0];lum[0]=s[0];mum[0]=s[0];num[0]=s[0];oum[0]=s[0];    for(int i=1;i<len;i++)    {        sum[i]=(sum[i-1]*31LL+s[i])%MOD;        kum[i]=(kum[i-1]*233LL+s[i])%MOD;        lum[i]=(lum[i-1]*107LL+s[i])%MOD;        mum[i]=(mum[i-1]*131LL+s[i])%MOD;        num[i]=(num[i-1]*1313LL+s[i])%MOD;        oum[i]=(oum[i-1]*1331LL+s[i])%MOD;    }    ans=0;    for(int i=n-1;i<len;i++)    {        if(i>=n)        {            long long f=(sum[i]-(sum[i-n]*p)%MOD+MOD)%MOD;            long long ff=(kum[i]-(kum[i-n]*pp)%MOD+MOD)%MOD;            long long fff=(lum[i]-(lum[i-n]*ppp)%MOD+MOD)%MOD;            long long ffff=(mum[i]-(mum[i-n]*pppp)%MOD+MOD)%MOD;            long long fffff=(num[i]-(num[i-n]*ppppp)%MOD+MOD)%MOD;            long long ffffff=(oum[i]-(oum[i-n]*pppppp)%MOD+MOD)%MOD;            int delta= max(max(max(find(f), find2(ff)), max(find3(fff), find4(ffff))), max(find5(fffff), find6(ffffff)));            if (delta)  ans++;        }        else         {            long long f=sum[i];            long long ff=kum[i];            long long fff=lum[i];            long long ffff=mum[i];            long long fffff=num[i];            long long ffffff=oum[i];            int delta= max(max(max(find(f), find2(ff)), max(find3(fff), find4(ffff))), max(find5(fffff), find6(ffffff)));            if(delta)ans++;        }    }    printf("%lld\n",ans);    return 0;} 
0 0