新疆网络赛 G 题 Query on a string 【kmp/暴力匹配 + 树状数组 + 思维】

传送门
//题意: 给定一个字符串每次询问一个区间, 问可以匹配多少个子串.
//思路: 因为子串的长度最大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);
                }
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值