【bzoj2384】[Ceoi2011]Match 特殊匹配条件的KMP+树状数组

题目描述

给出两个长度分别为n、m的序列A、B,求出B的所有长度为n的连续子序列(子串),满足:序列中第i小的数在序列的Ai位置。

输入

第一行包含两个整数n, m (2≤n≤m≤1000000)。 
第二行包含n个整数si,构成1,2,…,n的排列,1≤si≤n且si≠sj。 
第三行包含m个整数hi,表示建筑的高度(1≤hi≤109,1≤i≤m),所有的hi均不相同。 
每一行的整数之间用单个空格隔开。

输出

第一行包含1个整数k ,表示匹配的序列数目。
第二行包含k个整数,分别为在正确匹配的每个序列中与标志编号1 的条纹相对应的第1 栋建筑的编号。这些数字按升序排列,用空格隔开。如果k=0 ,第二行为空行。

样例输入

5 10
2 1 5 3 4
5 6 3 8 12 7 1 10 11 9

样例输出

2
2 6



题解

特殊匹配条件的KMP+树状数组

考虑:序列满足条件可以由 每个数前面比它小的数的个数 判定。

于是我们可以先预处理出每个数前面比它小的数应该有多少个。

然后如果暴力匹配的话肯定会TLE,于是想到KMP算法。

所以需要先求出next数组。

考虑KMP求next数组的过程:当满足条件时从前一个递推到后一个。那么可以使用树状数组维护比一个数小的数的个数,当当前小于该数的数的个数不等于应有的个数时就减少长度,并暴力将减掉的数从树状数组中删除。

由于每次next减少对应的是前面的next的增加,而next每次只增加1,因此对于每个字符的均摊时间复杂度是$O(\log m)$的。

然后求出next数组后就是匹配的过程,和求next类似,需要离散化。

因此总的时间复杂度为$O((n+m)\log m)$。貌似本题还有线性做法,然而不会= =

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 1000010
int m , a[N] , s[N] , v[N] , h[N] , t[N] , next[N] , f[N] , sta[N] , tot;
inline void add(int x , int a)
{
	int i;
	for(i = x ; i <= m ; i += i & -i) f[i] += a;
}
inline int query(int x)
{
	int i , ans = 0;
	for(i = x ; i ; i -= i & -i) ans += f[i];
	return ans;
}
int main()
{
	int n , i , j , p = 0;
	scanf("%d%d" , &n , &m);
	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &a[i]) , s[a[i]] = i;
	for(i = 1 ; i <= n ; i ++ ) v[i] = query(s[i]) , add(s[i] , 1);
	for(i = 1 ; i <= m ; i ++ ) scanf("%d" , &h[i]) , t[i] = h[i];
	memset(f , 0 , sizeof(f));
	for(i = 2 ; i <= n ; i ++ )
	{
		while(query(s[i]) != v[p + 1])
		{
			for(j = i - p ; j < i - next[p] ; j ++ ) add(s[j] , -1);
			p = next[p];
		}
		next[i] = ++p , add(s[i] , 1);
	}
	sort(t + 1 , t + m + 1);
	memset(f , 0 , sizeof(f));
	p = 0;
	for(i = 1 ; i <= m ; i ++ )
	{
		h[i] = lower_bound(t + 1 , t + m + 1 , h[i]) - t;
		while(p == n || query(h[i]) != v[p + 1])
		{
			for(j = i - p ; j < i - next[p] ; j ++ ) add(h[j] , -1);
			p = next[p];
		}
		p ++ , add(h[i] , 1);
		if(p == n) sta[++tot] = i - n + 1;
	}
	printf("%d\n" , tot);
	for(i = 1 ; i < tot ; i ++ ) printf("%d " , sta[i]);
	if(tot) printf("%d" , sta[tot]);
	return 0;
}
时间: 2024-11-19 16:30:14

【bzoj2384】[Ceoi2011]Match 特殊匹配条件的KMP+树状数组的相关文章

【POJ 3167】Cow Patterns (KMP+树状数组)

Cow Patterns Description A particular subgroup of K (1 <= K <= 25,000) of Farmer John's cows likes to make trouble. When placed in a line, these troublemakers stand together in a particular order. In order to locate these troublemakers, FJ has lined

BZOJ-1264 :[AHOI2006]基因匹配Match(树状数组+DP)

1264: [AHOI2006]基因匹配Match Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 904  Solved: 578[Submit][Status][Discuss] Description 基因匹配(match) 卡卡昨天晚上做梦梦见他和可可来到了另外一个星球,这个星球上生物的DNA序列由无数种碱基排列而成(地球上只有4种),而更奇怪的是,组成DNA序列的每一种碱基在该序列中正好出现5次!这样如果一个DNA序列有N种不同的碱基构成

BZOJ 1264 AHOI2006 基因匹配Match 动态规划+树状数组

题目大意:给定n个数和两个长度为n*5的序列,每个数恰好出现5次,求两个序列的LCS n<=20000,序列长度就是10W,朴素的O(n^2)一定会超时 所以我们考虑LCS的一些性质 LCS的决策+1的条件是a[i]==b[j] 于是我们记录a序列中每个数的5个位置 扫一下b[i] 对于每个b[i]找到b[i]在a中的5个位置 这5个位置的每个f[pos]值都可以被b[i]更新 于是找到f[1]到f[pos-1]的最大值+1 更新f[pos]即可 这个用树状数组维护 时间复杂度O(nlogn)

[BZOJ1264][AHOI2006]Match(DP+树状数组)

题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1264 分析: 考虑做一般的LCS的时候,更新结果的条件是a[i]==b[j]时候 于是可以先记下a数组中1~n每个数字出现的5个位置 然后依次扫描b[i],b[i]可以和对应a中数字的5个位置更新结果,即f[pos]=max(f[1],f[2],...,f[pos-1])+1 这个找最大的操作就用树状数组就行了

【BZOJ3413】匹配 离线+后缀树+树状数组

[BZOJ3413]匹配 Description Input 第一行包含一个整数n(≤100000). 第二行是长度为n的由0到9组成的字符串. 第三行是一个整数m. 接下来m≤5·10行,第i行是一个由0到9组成的字符串s,保证单行字符串长度小于等于10^5,所有字符串长度和小于等于3·10^6 Output 输出m行,第i行表示第si和S匹配所比较的次数. Sample Input 7 1090901 4 87650 0901 109 090 Sample Output 7 10 3 4 题

BZOJ1264 [AHOI2006]基因匹配Match 动态规划 树状数组

欢迎访问~原文出处--博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1264 题意概括 给出两个长度为5*n的序列,每个序列中,有1~n各5个. 求其最长公共子序列长度. 题解 我们发现这题的序列特殊性是关键! 我们只需要知道每一种数字在某一个序列中的5个位置,然后对于普通的LCS问题,我们只有在a[i] = b[j]的时候才会+1. 那么我们可以维护一个树状数组,在a序列中,我们一个一个位置扫过去,每次通过树状数组维护的前缀最大值来更新,然后因为修改不多,所以维护

[BZOJ1264][AHOI2006]基因匹配Match(DP + 树状数组)

传送门 有点类似LCS,可以把 a[i] 在 b 串中的位置用一个链式前向星串起来,由于链式前向星是从后往前遍历,所以可以直接搞. 状态转移方程 f[i] = max(f[j]) + 1 ( 1 <= j  < i && a[i] == b[j] ) ——代码 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 5 using namespace std; 6 7

【DP+树状数组】BZOJ1264-[AHOI2006]基因匹配Match

[题目大意] 给定n个数和两个长度为n*5的序列,两个序列中的数均有1..n组成,且1..n中每个数恰好出现5次,求两个序列的LCS. [思路] 预处理每个数字在a[i]中出现的五个位置.f[i]示以a[i]为末尾的最长公共子串(*这样就可以避免讨论交叉). 依次处理b[i],对于每个b[i]找到a[i]中的五个位置转移,用nowp表示,转移很简单:f[nowp]=max(f[nowp],query(nowp-1)+1),这里需要维护前缀最大值. 才知道前缀最大值可以用BIT来维护. 不过要注意

bzoj 1264: [AHOI2006]基因匹配Match (树状数组优化dp)

链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1264 思路: n大小为20000*5,而一般的dp求最长公共子序列复杂度是 n*n的,所以我们必须优化. 题目说了一个数会出现5次,那么我们可以预处理得到 第一个序列a[]每个数字分别在哪些位置, 因为求LCS的状态转移方程中当 s1[i-1] == s2[j-1]时,dp[i][j] = dp[i-1][j-1] + 1;只有当两个点相同时 值才会+1,我们可以对第二个序列b[]遍历一遍