2017 ACM-ICPC 亚洲区(乌鲁木齐赛区)网络赛-G: Query on a string (KMP+树状数组)

本文介绍了一种高效的字符串匹配与更新算法,用于处理大规模字符串集合的查询与修改操作。通过预处理和动态维护数据结构,算法能在常数时间内响应查询,并在字符串更新时快速调整匹配状态,特别适用于字符串长度变化不大的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

You have two strings SS and TT in all capitals.

Now an efficient program is required to maintain a operation and support a query.

The operation C~i~chC i ch with given integer ii and capital letter chch, changes the ii-th character of SS into chch.

The query Q~i~jQ i j asks the program to find out, in the substring of SS from the ii-th character to the jj-th one, the total number of TT appearing.

Input Format

The first line contains an integer TT, indicating that there are TT test cases.

For each test case, the first line contains an integer N~(N \leq 100000)N (N≤100000).

The second line is the string S~(|S| \leq 100000)S (∣S∣≤100000) and the third line is the string T~(|T| \leq 10)T (∣T∣≤10).

Each of the following NN lines provide a operation or a query as above descriptions.

Output Format

For each query, output an integer correponding to the answer.

Output an empty line after each test case.

样例输入复制

1
5
AABBABA
AA
Q 1 3
C 6 A
Q 2 7
C 2 B
Q 1 5

样例输出复制

1
2
0

题目来源

2017 ACM-ICPC 亚洲区(乌鲁木齐赛区)网络赛

编辑代码

 

题意:

给你一个A串一个B串,然后进行两种操作。

(1):给出p,q查询在A串中 p,q之间有多少B串(包括重叠的)。

(2):给出 p ch,将p位置的字符更改为ch。

分析:

这题的关键在于B串的字符少,不超过10,如果我们修改A串的某一个字符,那么对可能影响到答案只能是前后的10个,我们单独对其修改就行。我们用一个数组数组维护个数,如果匹配首字符标1,其他标0,那么如果我们改变l位置的字符,则前面l-blen+1的位置需要全部修改为0即可,然后跑一遍kmp匹配判断是否有出现新的匹配字符即可。

#include<iostream>
#include<string.h>
using namespace std;
typedef long long ll;
int read()//输入外挂
{
    int ret=0, flag=0;
    char ch;
    if((ch=getchar())=='-')
        flag=1;
    else if(ch>='0'&&ch<='9')
        ret = ch - '0';
    while((ch=getchar())>='0'&&ch<='9')
        ret=ret*10+(ch-'0');
    return flag ? -ret : ret;
}

const int N = 100005;
int nxt[11];
char S[N], T[11];
int slen, tlen;
const int MAXN=100000+5;//最大元素个数
int n;//元素个数
int c[MAXN],vis[MAXN];//c[i]==A[i]+A[i-1]+...+A[i-lowbit(i)+1]
//返回i的二进制最右边1的值
int lowbit(int i)
{
    return i&(-i);
}
//返回A[1]+...A[i]的和
ll sum(int x)
{
    ll sum = 0;
    while(x)
    {
        sum += c[x];
        x -= lowbit(x);
    }
    return sum;
}

//令A[i] += val

void add(int x, ll val)
{
    while(x <= slen)   //注意这里的n,是树状数组维护的长度
    {
        c[x] += val;
        x += lowbit(x);
    }
}
void getNext()
{
    int j, k;
    j = 0;
    k = -1;
    nxt[0] = -1;
    while(j < tlen)
        if(k == -1 || T[j] == T[k])
        {
            nxt[++j] = ++k;
            if (T[j] != T[k]) //优化
                nxt[j] = k;
        }
        else
            k = nxt[k];

}
/*
返回模式串在主串S中出现的次数  ,l表示从主串开始的字符,r表示结尾的字符,左开右闭
*/

int KMP_Count(int l,int r)
{
    //cout<<l<<" "<<r<<endl;
    for(int i=l; i<r-tlen+1; i++)
    {
        if(vis[i])
        {
            vis[i]=0;
            add(i+1,-1);
        }
    }
    int ans = 0;
    int i=l, j = 0;

    if(slen == 1 && r-l+1 == 1)
    {
        if(S[0] == T[0])
            return 1;
        else
            return 0;
    }

    for(i=l; i < r;)
    {

        while(j > 0 && S[i] != T[j])
        {
            j = nxt[j];

        }


        if(S[i] == T[j])
            j++;
        i++;
        if(j == tlen)
        {
            ans++;
            //cout<<i-tlen+1<<"*"<<j<<endl;
            add(i-tlen+1,1);
            vis[i-tlen]=1;
            j = nxt[j];///不可重叠的j=0

        }
    }

    return ans;
}

int main()
{

    int TT;
    int l,r;
    int n;
    char ch,w;
    TT=read();
    while(TT--)
    {
        memset(c,0,sizeof(c));
        memset(vis,0,sizeof(vis));
        n=read();
        scanf("%s%s",&S,&T);

        tlen = strlen(T);
        slen = strlen(S);
        getNext();

        KMP_Count(0,slen);

        while(n--)
        {

            scanf(" %c",&ch);

            if(ch=='Q')
            {
                l=read();
                r=read();
                if(r-l+1<tlen)
                    printf("0\n");

                else
                    printf("%d\n",sum(r-tlen+1)-sum(l-1));

            }
            else
            {
                l=read();
                scanf(" %c",&w);
                if(S[l-1]!=w)
                {
                    S[l-1]=w;
                    KMP_Count(max(0,l-1-tlen+1),min(slen-1,l-1+tlen-1)+1);
                }
            }
        }
		puts("");

    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值