zoukankan      html  css  js  c++  java
  • [bzoj1892][bzoj2384][bzoj1461][Ceoi2011]Match/字符串的匹配_KMP_树状数组

    2384: [Ceoi2011]Match 1892: Match 1461: 字符串的匹配

    题目大意

    数据范围


    题解

    很巧妙的一道题呀。

    需要对$KMP$算法有很深的理解才行。

    首先我们需要发现,要求的这个东西跟字符串匹配有点像。

    我们在单个模式串匹配的时候用到的$KMP$算法,合法匹配条件是两个字符完全相同。

    但是这个题本质上就是要求子串离散化之后相同。

    如果两个串离散化之后完全相同,等价于一个条件,就是每个数前面比它小的个数通通相等。

    这是显然的。

    所以我们尝试改变$KMP$的匹配模式,并且用树状数组维护长串的这个值。

    先假设,所有数字两两不同。

    对于要求离散化后的串,每个位置弄一个$f_i$表示这个串中,第$i$个位置前面有多少个比$b_i$小的。

    我们把如图红色位置加入树状数组

    然后我们查询$i$位置,有多少比$a_i$小的,跟$f_{nxt[i-1]}$相比。

    如果相等表示这个位置可以匹配,如果不能,我们就把

    $i-nxt_{i-1}$到$i-nxt_{nxt_{i - 1}}$。

    这样就可以了。

    如果离散化之后不完全相等的话,我们就考虑维护出来$i$前面和$b_i$相等的有多少个,再查就行了。

    代码

    #include <bits/stdc++.h>
    
    #define N 1000010 
    
    using namespace std;
    
    int tree[N], a[N], b[N], c[N], rk[N], bfr[N], nxt[N], ans[N];
    
    int n, m;
    
    char *p1, *p2, buf[100000];
    
    #define nc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++ )
    
    int rd() {
    	int x = 0, f = 1;
    	char c = nc();
    	while (c < 48) {
    		if (c == '-')
    			f = -1;
    		c = nc();
    	}
    	while (c > 47) {
    		x = (((x << 2) + x) << 1) + (c ^ 48), c = nc();
    	}
    	return x * f;
    }
    
    inline int lowbit(int x) {
    	return x & (-x);
    }
    
    void update(int x, int val) {
    	for (int i = x; i <= m; i += lowbit(i))
    		tree[i] += val;
    }
    
    int query(int x) {
    	int ans = 0;
    	for (int i = x; i; i -= lowbit(i))
    		ans += tree[i];
    	return ans;
    }
    
    int main() {
    	n = rd(), m = rd();
    	for (int i = 1; i <= n; i ++ )
    		a[i] = rd(), rk[a[i]] = i;
    	for (int i = 1; i <= n; i ++ )
    		bfr[i] = query(rk[i]), update(rk[i], 1);
    	for (int i = 1; i <= m; i ++ )
    		b[i] = rd(), c[i] = b[i];
    	memset(tree, 0, sizeof tree);
    
    	// for (int i = 1; i <= n; i ++ )
    		// printf("%d ", bfr[i]);
    	// puts("");
    
    	for (int i = 2, j = 0; i <= n; i ++ ) {
    		while (query(rk[i]) != bfr[j + 1]) {
    			for (int k = i - j; k < i - nxt[j]; k ++ )
    				update(rk[k], -1);
    			j = nxt[j];
    		}
    		if (query(rk[i]) == bfr[j + 1]) {
    			update(rk[i], 1);
    			j ++ ;
    		}
    		nxt[i] = j;
    	}
    
    	// for (int i = 1; i <= n; i ++ ) {
    		// printf("%d ", nxt[i]);
    	// }
    	// puts("");
    
    	sort(c + 1, c + m + 1);
    	memset(tree, 0, sizeof tree);
    
    	for (int i = 1, j = 0; i <= m; i ++ ) {
    		// printf("i-> %d
    ", i);
    		b[i] = lower_bound(c + 1, c + m + 1, b[i]) - c;
    		// printf("%d
    ", b[i]);
    		// printf("%d %d %d
    ", j, query(b[i]), bfr[j + 1]);
    		while (j == n || query(b[i]) != bfr[j + 1]) {
    			for (int k = i - j; k < i - nxt[j]; k ++ ) {
    				update(b[k], -1);
    			}
    			j = nxt[j];
    		}
    		if (query(b[i]) == bfr[j + 1]) {
    			update(b[i], 1);
    			j ++ ;
    		}
    		if(j == n)
    			ans[ ++ ans[0]] = i - j + 1;
    	}
    
    	printf("%d
    ", ans[0]);
    	for (int i = 1; i < ans[0]; i ++ )
    		printf("%d ",ans[i]);
    	if(ans[0])
    		printf("%d
    ", ans[ans[0]]);
    	return 0;
    }
    

    小结:好题啊,这个题真的不好想,我看题解都看了半天.......

  • 相关阅读:
    微软推出的免费新书《Introducing Microsoft SQL Server 2012》
    关于Installshield中Ie8\Ie9\SQL Server 2008 R2 Native Client等Prq文件在线下载地址
    PowerDesigner批量生成日期型、中文字符型、数字型测试数据
    在服务器上使用第三方独立组件对Word/Excel进行编程
    文明源自谎言
    中文写程序,何陋之有?
    在线网摘收藏?让Google来吧!
    下载文件时根据MIME类型自动判断保存文件的扩展名
    谨慎注意WebBrowser控件的DocumentCompleted事件
    你的命运谁攥着?
  • 原文地址:https://www.cnblogs.com/ShuraK/p/11238953.html
Copyright © 2011-2022 走看看