zoukankan      html  css  js  c++  java
  • 【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
    " , tot);
    	for(i = 1 ; i < tot ; i ++ ) printf("%d " , sta[i]);
    	if(tot) printf("%d" , sta[tot]);
    	return 0;
    }
    

     

  • 相关阅读:
    Eclipse 代码模板
    Eclipse 安装插件
    Eclipse 任务管理
    Eclipse 添加书签
    Eclipse 重构菜单
    Eclipse 浏览(Navigate)菜单浏览 Eclipse 工作空间
    Eclipse 查找
    Eclipse 悬浮提示
    Eclipse 快速修复
    Eclipse 内容辅助
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7573120.html
Copyright © 2011-2022 走看看