CF914F Substrings in a String
传送门
bitset的神奇用法,乍眼一看好像是KMP,发现每次都要预处理next数组,时间复杂度爆了。
考虑bitset(玄学),我们记录这样一个bitset $ a[i][j],其中a[i][j]=1时,s[j]−′a′=i$
大概把字符串abcabcabc能转换成这样一个东西:
a |
1 |
0 |
0 |
1 |
0 |
0 |
1 |
0 |
0 |
b |
0 |
1 |
0 |
0 |
1 |
0 |
0 |
1 |
0 |
c |
0 |
0 |
1 |
0 |
0 |
1 |
0 |
0 |
1 |
修改非常简单,只要把原来的1变成0,再把新加进的变成1
考虑如何查询,假设我们查询的是abc,我们把a,b,c挪到同一列(用位移操作即可完成),如下:
a |
1 |
0 |
0 |
1 |
0 |
0 |
1 |
0 |
0 |
b |
1 |
0 |
0 |
1 |
0 |
0 |
1 |
0 |
0 |
c |
1 |
0 |
0 |
1 |
0 |
0 |
1 |
0 |
0 |
用ans去和每一列做and运算,操作完之后,ans大概长成这个样子:
发现只有在i位后面出现abc字符串,ans[i]才为1。
于是我们发现答案为∑i=lr−len+1ans[i](len为查询的字符串的长度),前缀和相减即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| #include <bits/stdc++.h> #define MAXN 100005 #define MAXM 27 using namespace std; bitset<MAXN>B[MAXM]; inline int read(){ int x=0,f=1; char ch=getchar(); while (ch<'0'||ch>'9'){ if (ch=='-') f=-1; ch=getchar(); } while (ch>='0'&&ch<='9'){ x=(x<<3)+(x<<1)+(ch^'0'); ch=getchar(); } return x*f; } inline char gc(){ char ch=getchar(); while (ch<'a'||ch>'z') ch=getchar(); return ch; } int main(){ char ch[MAXN]; scanf("%s",ch); int n=strlen(ch); for (register int i=1;i<=n;++i){ B[ch[i-1]-'a'][i]=1; } int q=read(); while (q--){ int opr=read(); if (opr==1){ int i=read(); char c=gc(); B[ch[i-1]-'a'][i]=0; ch[i-1]=c; B[ch[i-1]-'a'][i]=1; } else { int l=read(),r=read(); char y[MAXN]; scanf("%s",y); int len=strlen(y); bitset<MAXN>ans; ans.set(); for (register int i=0;i<len;++i){ ans&=(B[y[i]-'a']>>i); } printf("%d\n",max(0,(int)(ans>>l).count()-(int)(ans>>(r-len+2)).count())); } } }
|
听说正解是分块+SAM,害怕