2017 ACM-ICPC 亚洲区(乌鲁木齐赛区)网络赛(G. Query on a string)kmp+线段树

题目大意,给予两个串,S和T, S很长,T很短(这个很重要), 有n次操作,操作分为两种形式, 一种是把S的第i个字符替换, 第二中是截取S中l到r的一段,求T在这段出现的次数。

思路:先构建一个线段树,所有节点初始化为0,然后对S和T进行kmp匹配,如果T在S中出现,就把T这次出现的T【0】位置,所对应的线段树节点变为1。

例如: S为‘ADADBBAD', T为’AD', 那么线段树的节点为1,0,1,0,0,0,1,0 。

那么对于每次查询,就是线段树中 sum(l, r)- sum(r-(len(s))+1 ,r),的值。

对于每次修改(假设修改了id位置的字符), 因为T的长度最多是10, 直接把S串中id-len(T) 到id, 这段字符取出,和T进行匹配,然后去修改线段树对应这段的数据就可以了。


#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
#define Max_N (100000+100)
#define updata1 updata
int next1[20];
int tree[4*Max_N];
int len4;
void init(int n_) {
    len4 = 1;
    while (len4 < n_) len4 *= 2;
}
void updata(int k, int s)
{
    int n = len4;
	k += n - 1;
    tree[k] = s;
    while (k > 1) {
        k = (k) / 2;
        tree[k] = tree[2*k] + tree[2*k+1];
    }
}
int  query(int node, int l ,int r, int L, int R)
{
	int mid = l + (r - l) / 2;
	if (L > R) return 0;
	if (R < l || L > r) return 0;
	if (L <= l && r <= R) return tree[node];
	return query(node*2, l, mid, L, R) + query(node*2+1, mid+1, r, L, R);
}
void kmp_pre(char x[], int m, int next1[])
{
    int i, j;
    j = next1[0] = -1;
    i = 0;
    while (i < m)
    {
        while(-1 != j && x[i] != x[j]) j = next1[j];
        next1[++i] = ++j;
    }
}
int num;
void KMP_Count(char x[], int len1, char y[], int len2)
{
	int i, j;
	int ans = 0;
	i = j = 0;
	num = 0;
	while (i < len2) {
		while (-1 != j && y[i] != x[j]) j = next1[j];
		i++; j++;
		if(j >= len1) {
			j = next1[j];
			updata(i - len1+1, 1);
		}
	}
	return;
}
void KMP_Count1(char x[], int len1, char y[], int len2, int x1)
{
	int i, j;
	int ans = 0;
	i = j = 0;
	num = 0;
	while (i < len2) {
		while (-1 != j && y[i] != x[j]) j = next1[j];
		i++; j++;
		if(j >= len1) {
			j = next1[j];
			updata(i - len1+1 + x1, 1);
		}
	}
	return;
}

int T, n;
char s[Max_N];
char t[15];
char s2[15];
char q[5];
int id;
int l, r;
int main()
{
	scanf("%d", &T);
	while (T--) {
		memset(tree, 0, sizeof(tree));
		scanf("%d", &n);
		scanf("%s", s);
		scanf("%s", t);
        int len1 = strlen(s);
		int len2 = strlen(t);
        init(len1);
		kmp_pre(t, len2, next1);
		KMP_Count(t, len2, s, len1);
		//cout << "ss" << endl;
		for (int i = 0; i < n; i++) {
			scanf("%s", q);
			//cout << "ss" << endl;
			if (q[0] == 'Q') {
				scanf("%d%d", &l, &r);
				printf("%d\n", query(1, 1, len4, l, r) - query(1, 1, len4, r - len2 + 2, r));
			}
			else {
				scanf("%d%s", &id, q);
				s[id-1] = q[0];
				int l1 = max(1, id-len2);
				int r1 = min(len1, id + len2);
				int len3 = 0;
				for (int j = l1-1; j <= r1-1; j++) {
					s2[len3++] = s[j];
					updata(j+1,0);
				}
				KMP_Count1(t, len2, s2, len3, l1-1);
			}
			cout << s << endl;
			for (int i = 1; i <= len1; i++)
				cout << query(1, 1, len4, i, i) << endl;
			//cout << s << endl;
		}
		
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ACM-ICPC(国际大学生程序设计竞)是一项面向大学生的计算机编程竞,涉及算法和数据结构等领域。在比中,选手需要解决一系列编程问题,使用合适的算法和数据结构来实现正确和高效的解决方案。 对于整理ACM-ICPC模板,以下是一些建议: 1. 了解比要求:首先,你需要了解ACM-ICPC的具体要求和规则。这包括了解比所涉及的算法和数据结构,以及题目的类型和难度等。 2. 收集资料:收集与ACM-ICPC相关的资料,包括经典算法和数据结构的实现代码、常见问题的解题思路等。可以参考教材、博客、论文等资源。 3. 整理模板:将收集到的资料整理成模板。可以按照算法和数据结构的分类进行整理,例如排序算法、图算法、字符串算法等。对每个模板,添加必要的注释和示例代码,以便理解和使用。 4. 测试代码:对每个模板编写测试代码,确保它们的正确性和可靠性。可以使用已知的测试用例或自行设计测试用例。 5. 更新与扩充:定期更新和扩充模板,以适应ACM-ICPC中新出现的算法和数据结构。同时,根据自己的经验和理解,对模板进行优化和改进。 6. 练习和复习:在比之前,利用整理好的模板进行练习和复习。尝试解决一些经典问题,使用模板中的算法和数据结构进行实现,并进行优化。 希望这些建议对你整理ACM-ICPC模板有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值