codeforecs 316G2 哈希+优化

https://codeforces.com/contest/316/problem/G2

给定一个母串$s$,问母串$s$有多少本质不同的子串$t$是“好”的。

  一个字符串$t$是好的,仅当$t$满足了所有的$n$个条件。

  第$i$个条件用一个三元组$(p_i,L_i,R_i)$来描述。

  其中$p_i$为一个字符串,$L_i,R_i$为整数,且$L_ileq R_i$。

  仅当字符串$t$在$p_i$中出现次数在$L_i$到$R_i$之间时,它是"好"的。

  $|s|,|p_i|leq 2 imes 10^3,nleq 10$

专门说一下G2吧,G2感觉是裸哈希+暴力,一眼复杂度$O(n*(|s|^2+sum|p|^2)*hashmap)$

但是ac过程十分艰苦,首先,如果是单哈希$mod1e9+7$,基本上肯定会碰撞导致wa

(1e9的哈希空间,基本上1e6次访问就能发生上百次碰撞)

然后换成ull单哈希,感谢cf爸爸不卡ull.

但是还是T,

然后换成u_map和u_set,但是还是T

然后加入优化

1.如果枚举的p的子串长度>s的长度,break

2.枚举p子串起点,再枚举p子串终点,如果当前p的子串不存在于s中,break,后面的必不存在(和上面的其实重复了)

3.从枚举s的子串改为枚举p的子串(这个实际上应该没快多少)

4.开两个set保存s满足前i个限制的子串,每次滚动指针,s1,s2,

其中s1保存了当前满足前i个限制的所有子串

如果当前限制l==0,那么枚举p的所有子串,所有出现次数>r的,从s1中删除,然后暂时不交换指针

如果当前限制l>0,那么枚举p的所有子串,只把出现次数满足约数的放入s2,然后交换s1,s2指针

以上的优化其实都挺片面,感谢cf爸爸不卡之恩

#include<bits/stdc++.h>
#define ull unsigned long long
#define fi first
#define se second
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
using namespace std;//head
const int maxn=2e3+10,maxm=1e3+10;
int n,m;
const ull decm=131;
ull pw[maxn];
void init(int n=maxn-5){pw[0]=1;rep(i,1,n) pw[i]=pw[i-1]*decm;}
class strhash{public:
  ull hs[maxn],len;
  void calhs(char *s,int n){len=n;rep(i,0,len-1)hs[i+1]=hs[i]*decm+s[i];}
  inline ull geths(int a,int b){return hs[b]-hs[a-1]*pw[b-a+1];}
}mps;
unordered_map<ull,int> cnt;
unordered_set<ull> vis,s1,s2;
char s[maxn];
int l,r;
int main() {IO;
  init(maxn-5);cin>>s>>n;
  m=strlen(s);mps.calhs(s,m);
  rep(i,1,m)rep(j,i,m) {
    ull x=mps.geths(i,j);
    if(!vis.count(x)){
      vis.insert(x);
      s1.insert(x);
    }
  }
  if(n==0){
    cout<<vis.size();
    return 0;
  }
  int ans=0;
  unordered_set<ull> &sub=s1,&tmp=s2;
  rep(i,1,n){
    cin>>s>>l>>r;
    int len=strlen(s);cnt.clear();
    mps.calhs(s,len);
    rep(j,1,len)rep(k,j,len) {
      if(k-j+1>m) break;
      ull x=mps.geths(j,k);
      if(!vis.count(x)) break;
      cnt[x]++;
    }
    if(l==0){
      for(auto x:cnt)if(sub.count(x.fi)&&x.se>r)sub.erase(x.fi);
      if(i==n)ans=sub.size();
      continue;
    }
    for(auto x:cnt)if(sub.count(x.fi)&&x.se>=l&&x.se<=r)tmp.insert(x.fi);
    sub.clear();
    if(i==n)ans=tmp.size();
    swap(tmp,sub);
  }
  cout<<ans<<endl;
}
原文地址:https://www.cnblogs.com/nervendnig/p/11358980.html