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;
    }
    

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

  • 相关阅读:
    Jzoj4822 完美标号
    Jzoj4822 完美标号
    Jzoj4792 整除
    Jzoj4792 整除
    Educational Codeforces Round 79 A. New Year Garland
    Good Bye 2019 C. Make Good
    ?Good Bye 2019 B. Interesting Subarray
    Good Bye 2019 A. Card Game
    力扣算法题—088扰乱字符串【二叉树】
    力扣算法题—086分隔链表
  • 原文地址:https://www.cnblogs.com/ShuraK/p/11238953.html
Copyright © 2011-2022 走看看