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_i\leq R_i$。
仅当字符串$t$在$p_i$中出现次数在$L_i$到$R_i$之间时,它是"好"的。
$|s|,|p_i|\leq 2\times 10^3,n\leq 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; }