HDU - 4821 (暴力的复杂度计算)

本题目意思:

给定一个长度为1E5的串,给定N,M,让统计所有的连续子串,满足串长为N*M,而且这N个长为M的串两两不相同,输出个数。

分析:

本题目明显使用哈希来做的,因为在LONG LONG 内散列不超过 1E5个子串,出现冲突的概率极低。

然后不能从头暴力算出第一个长为N*M的串每个长M的哈希值,然后递推计算后面的每个长为N*M串的hash值,这样复杂度为(len - N*M)*(N*log(M))很容易被卡掉的,算一下就清楚。

那么,暴力的方法是:

记hash(1->m),为远串位置从1->m的子串的hash值,先计算出所有的 hash( k*m+1 , (k+1)*m),

那么第一个长为N*M的串的hash集合为上面的K 取 0 - > n-1; 那么  位置在 m+1 - > (n+1)*m的子串的哈希集合就是在前面的基础上在前面去掉一个,在后面加上一个,

那么用map维护hash集合的变动,每个这样的串的复杂度为o(log(m))

所以加上递推初始所有hash值,总复杂度 m*(len / m) + (len - n*m)*log(m),这样的复杂度组大不超过 len*log(M),所以可以ac;

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <cmath>
#include <cctype>
#define rep1(i,x,y) for(int i=x;i<=y;i++)
using namespace std;
typedef long long LL;
const int base = 131;
const int N = 101010;
const LL mod = 1e16;

char str[N];
int n,m;
LL ha[N];
map<LL,int> M;
int main()
{
    memset(str,0,sizeof(str));
    while(scanf("%d %d",&n,&m)==2){
       scanf("%s",str+1);
       int len = strlen(str+1), all = len/m; LL tem = 0;
       rep1(i,1,len){
            tem=(tem*base+str[i])%mod; if(i%m == 0) {ha[i/m]=tem; tem=0;}
       }
       LL ba = 1; rep1(i,1,m-1) ba=ba*base%mod;
       int res = 0;
       rep1(i,1,m){
            if(i + n*m -1 > len) break;
            M.clear();
            if(i > 1){
               rep1(j,1,all){
                   ha[j]=(ha[j]-ba*str[(j-1)*m+i-1]+mod)%mod;
                   ha[j]=(ha[j]*base+str[j*m+i-1])%mod;
                   if(j<=n) M[ha[j]]++;
               }
            }
            else rep1(j,1,n) M[ha[j]]++;
            if(M.size() == n) res++;
            for(int st = i+n*m-1+m,j=1; st<=len;st+=m,j++){
                 M[ha[j]]--; M[ha[j+n]]++;
                 if(M[ha[j]] == 0) M.erase(ha[j]);
                 if(M.size() == n) res++;
            }
       }
       printf("%d\n",res);
    }
    return 0;
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值