首页 > 其他分享 >[CEOI2011] Matching 题解

[CEOI2011] Matching 题解

时间:2023-08-23 09:12:42浏览次数:55  
标签:nxt 匹配 CEOI2011 题解 int 1000006 数组 Matching

[CEOI2011] Matching 题解

题外话:

看了其他人题解后作为初学 $kmp$ 的我非常蒙,因为对这个算法的核心掌握不太好,不知道怎么维护动态的序列,因此写下此题解共享经验,建议只会打模板的看看。

参考资料:

https://www.cnblogs.com/fusiwei/p/11944975.html

思路引导:

看到数据范围,又和真实值没有太大关系,立即离散化,不再赘述。

首先,我们能看出这是一个类似匹配的问题,即对于任意一个和 p 相同长度的 a 的子串,要求其排名序列和 p 一毛子一样。那么这样的话,一般情况下我直接在 a 前面接上 p 然后跑一遍模板 $kmp$ 就好了。

但是出现了意外,原因是:我的主串 a 的子串和模式串 p 不一定一模一样(大多数不一样),但是仍应该匹配。

所以我们考虑如何让 a 的某个子串和 p 一模一样达成匹配。

如果您看了前面的题解,那么可以知道一种方式:将 a 的子串和 p 的第 i 位表示为 i 位置之前比他大的数有多少个。很显然这样就能达成匹配。例子的话,其他题解给出了,这里不再重复。并且个人认为,树状数组维护这个东西是显而易见的。

然后就是困扰我许久的问题:用 $kmp$ 算法怎么处理动态的数组呢?(为什么是动态?因为我们发现对于同一个位置 i,考虑的子串不同,那么他对应的排名也不同,相应地,他前面比他大的数字个数也不同。

首先,很明显的是,我们模式串 p 肯定不会改变吧。

其次,通过上文的参考资料可以得知, nxt 数组只和模式串 p 有关系(因为跳 nxt 数组时,只是模式串在跳)。于是我们可以先求出这个数组。

那么显而易见的,我只要维护好在跳 nxt 数组时的主串末尾变化即可。

不懂?我们用下图来辅助一下。

假设我们上一次匹配到了 j 位置,正在匹配 i 位置。

首先我们要尝试一下 $h_i$ 这里能否和 $P_{j+1}$ 这个位置达成匹配,准确来说,是看他们的位置上的排名是否相等,很显然,如果相等的话,i 位置的答案就是 $j+1$。

要是无法达成匹配,我们就要跳 $p_j$ 的 nxt 数组,尝试能否达成匹配。但是显然,我在跳 nxt 数组时,相应的后缀的长度会短,我应该在主串中删除不在变短的后缀中的数字以免影响排名,而这些数字所在的区间正是 $[i-j,i-nxt[j]-1]$ ,我们用树状数组也可以强制删除噻。

最后达成匹配,看他的长度是否和 p 的长度相等,相等的话就说明完全匹配上了。但此时你的指针位置在这个合法区间的末尾,因此答案应该加入 $i-n+1$。

代码:

个人感觉思路很重要,建议理解后自己写。

#include<bits/stdc++.h>
#define int long long
const int mod=1e9+7;
using namespace std;
int p[1000006];
int h[1000006];
int c[1000006];
int lsh[1000006];
int len[1000006];
int cnt[1000006];//可以看作是数->排名的一种映射,只和p有关
int nxt[1000006];
vector<int>ans;
int lowbit(int x){
	return x&(-x);
}
void add(int pos,int val){
	for(int i=pos;i<=1000000;i+=lowbit(i)){
		c[i]+=val;
	}
} 
int query(int pos){
	int ret=0;
	for(int i=pos;i>=1;i-=lowbit(i)){
		ret+=c[i];
	}
	return ret;
} 

signed main(){
	ios::sync_with_stdio(false);
	int n,m;
	cin >> n >> m;
	for(int i=1;i<=n;i++){
		int k;
		cin >> k;
		p[k]=i;
	}
	for(int i=1;i<=m;i++){
		cin >> h[i];
		lsh[i] = h[i];
	}
	sort(lsh+1,lsh+1+m);
	for(int i=1;i<=m;i++){
		h[i]=lower_bound(lsh+1,lsh+1+m,h[i])-lsh;
	} 
	//离散化结束,开始尝试kmp
	cnt[n+1] = -0x7fffffff;
    for(int i=1;i<=n;i++) {
    	cnt[i] =query(p[i]);
    	add(p[i], 1);
	}//最前面的串的排位 
	for(int i = 1; i <= m; i++) {
		c[i] = 0;
	}//清空 
	int j=0;
	for (int i=2;i<=n;i++) {
		while (j&&query(p[i])!=cnt[j + 1]) {
			for(int k=i-j;k<=i-nxt[j]-1;k++){
				add(p[k], -1);
			}
			j=nxt[j];
		}
		if (query(p[i])==cnt[j+1]) {
			j++; 
			add(p[i], 1);
		}
		nxt[i] = j;
	}
//	for(int i=1;i<=n+1;i++){
//		cout<<cnt[i]<<" ";
//	}
//	cout<<endl;
	for (int i=1;i<=m;i++) {
		c[i]=0;
	}
	j=0;
	for (int i=1;i<=m;i++) {
		while (j && query(h[i])!=cnt[j + 1]) {
			for (int k=i-j;k<=i-nxt[j]-1; k++){
				add(h[k],-1);
			}
			j = nxt[j];
		}
		if (query(h[i])==cnt[j+1]) {
			j++;
			add(h[i], 1);
		}
		if (j==n){
			ans.push_back(i-n+1);
		}
	}
    printf("%d\n",ans.size());
    for (int i=0;i <ans.size(); i++) {
        printf("%d ",ans[i]);
	}
	
}

标签:nxt,匹配,CEOI2011,题解,int,1000006,数组,Matching
From: https://www.cnblogs.com/linghusama/p/17650131.html

相关文章

  • 【题解】洛谷 P1002 [NOIP2002 普及组] 过河卒
    原题链接解题思路这是一道经典的动态规划题目。如果尝试使用深度优先搜索(dfs)或广度优先搜索(bfs)做就会获得TLE(注意数据范围)。于是我们想到了更为高级的动态规划(DynamicProgramming,dp)。简略介绍动态规划算法的核心思想:把原问题分解为相对简单的子问题的方式求解复杂问题。......
  • P8772 [蓝桥杯 2022 省 A] 求和 题解
    蒟蒻第一次发题解qwq$S=a_1\timesa_2+a_1\timesa_3+a1\timesa_n+a_2\timesa_3+···+a_n-2\timesa_n-1+a_n-1\timesa_n$从样例来看41369这道题就是要求$1\times3+1\times6+1\times9+3\times6+3\times9+6\times9$我们可以把这个算式分成几个部分$(1\t......
  • ABC314EX 题解
    模拟退火的题解。题目的难点在于如何计算点到所有线段的距离。我们可以通过求线段的直线解析式,然后计算与其垂直的直线的斜率,将随机出来的点带入求得直线解析式,然后求两直线交点,判断是否在线段上进行分讨即可,但是我可能写挂了,所以改成用的向量。我们看到有三种情况,我们可以分......
  • YACS 2023年8月月赛 甲组 T3 金字塔分割 题解
    看到这题,自然的想到DP啦!如果设$f_{i,j}$为到第$i$个位置前面的都合法且最后一段和为$j$是否可行,那么转移十分显然,但是状态会炸。此时我们考虑在状态上进行优化来减少时间,把$f_i$设为到第$i$个位置分段数量最多的情况下且最后一段和最少的和,以及能分成几段就好了。......
  • [USACO JAN 2011]交通灯 题解
    题意很清晰,直接跑SPFA求最短路。只是我们在松弛操作时,需要注意从\(u\)是否可以到达\(v\)。怎么判断呢?请移步下面三个部分。Part1先解释一下,下面点\(i\)的信息分别为以下变量:color表示颜色,1表示蓝色,0表示紫色num表示初始状态持续时间t1表示蓝色状态持续时间......
  • CF1818 & 1817 题解
    Div2A容易发现最后要存活下来一定要每次和\(1\)号做出相同的选择,直接数就好了.Div2B容易发现当\(n\)为奇数的时候无解。考虑\(n\)为偶数的情况怎么构造,有一种方案是在\(a_i=i\)的基础上调整,交换一下\(a_{2i-1}\)和\(a_{2i}\),证明考虑左右端点的奇偶性。Div2C&......
  • P9236 [蓝桥杯 2023 省 A] 异或和之和题解
    思路题目给我们一个数组\(a\),那么我们可以算出其异或前缀和\(sum\)。我们知道,算出\([l,r]\)的异或和可以这样计算:\(sum_r\oplussum_{l-1}\)。那么问题就转换为了\(sum_{0\simn}\)这\(n+1\)个数字两两异或之和(当然\(sum_i\oplussum_j\)和\(sum_j\oplussum......
  • 8.22 [CSP-S 2021] 交通规划 题解
    #include<bits/stdc++.h>usingnamespacestd;usingpii=pair<int,int>;constexprintN=3e5+5,S=2e3+5,K=1e2+5,INF=0x3f3f3f3f;intn,m,T,poi[N];inthed[N],nxt[N<<2],rch[N<<2],val[N<<2],idx;vo......
  • P9562 [SDCPC2023] G-Matching
    思路易发现,如果\(i\)和\(j\)可以连边,\(j\)和\(k\)可以连边,那\(i\)和\(k\)也可以连边,如果\(x\)不能和\(i\)连边,那\(x\)同样不能和\(j,k\)连边。所以我们可以考虑把所有可以连边的放在一起,这样就把所有点分成了若干部分,并且每个部分不可能连边,必然是分割开的。......
  • P3089 题解
    P3089令\(f_1[i][j]\)表示向右跳,从\(j\)跳到\(i\)的最大总得分,有状态转移方程:\[f_1[i][j]=\displaystyle\max_{k<j<i,x_j-x_k\lex_i-x_j}\{f_1[j][k]+s_i\}\]将\(s_i\)看作定值,整理得\(f_1[i][j]=\displaystyle\max_{k<j<i,x_j-x_k\lex_i-x_j}\{f_1[j][......