传送门
//题意: 给定一个字符串每次询问一个区间, 问可以匹配多少个子串.
//思路: 因为子串的长度最大10. 那么每次修改最多只修改到10次. 所以我们可以直接暴力匹配就是了. 对于主串, 如果从某个开头可以匹配, 那么设那个开头的位置为1. 比如说样例就是 1000000. 这样对于询问的一个区间, 我们直接求前缀和就是了. 还有就是需要注意的几个小细节, 请看代码.
注意分清下标的关系…
AC Code
const int maxn = 1e5+10;
int cas=1;
char zc[maxn],pp[25];
int Next[maxn],c[maxn];
int vis[maxn];
int n,m;
void add(int x,int val){ for(;x<=n;x+=x&-x) c[x]+=val;}
int getsum(int x){ int res=0;for(;x;x-=x&-x) res+=c[x];return res;}
void getnext(char *s,int len)
{
int t1 = 0,t2 ;
Next[0] = t2 = -1;
while(t1<len){
if(t2 == -1 || s[t1] == s[t2])
Next[++t1] = ++t2;
else t2 = Next[t2];
}
}
void kmp(char *zc,char *pp,int len1,int len2)
{
int i = 0 , t = 0;
while(i<len1){
while(t != -1 && zc[i] != pp[t]) t = Next[t];
i++; t++;
if(t == len2){
vis[i-t+1] = 1;
add(i-t+1,1);
t = Next[t];
}
}
}
void solve()
{
Fill(c,0); Fill(Next,0); Fill(vis,0);
int op; scanf("%d",&op);
scanf("%s",zc); n = strlen(zc);
scanf("%s",pp); getnext(pp,strlen(pp));
kmp(zc,pp,strlen(zc),strlen(pp)); //kmp处理第一阶段
m = strlen(pp);
while(op--){
char s1[5]; scanf("%s",s1);
char s2[5]; int l,r;
if(s1[0] == 'Q'){
scanf("%d%d",&l,&r);
if(r-l+1 < m) printf("0\n");
else {
r -= m-1; //想想这里为什么要减m+1?
printf("%d\n",getsum(r)-getsum(l-1));
}
}
else{
scanf("%d%s",&l,s2);
zc[l-1] = s2[0];
int tmp = (l-m) >= 0 ? l-m : 0; //计算起点
for(int i=tmp; i <= l-1 ;i++){ //暴力修改,也可以kmp修改
bool flag = true;
for(int j=0;j<m;j++){
if(zc[i+j] != pp[j] || i+m>n ){ //看是否满足条件
flag = false;
break;
}
}
if(flag && !vis[i+1]) { //记住需要一个标记数组标记当前位是否可以匹配
//过, 这样的操作是不一样的. 然后更新树状数组.
add(i+1,1);
vis[i+1] = 1;
}
else if(!flag && vis[i+1]){
vis[i+1] = 0;
add(i+1,-1);
}
}
}
}
}