• 题解 P4696 【[CEOI2011]Matching】


    (Large exttt{P4696})

    标签:KMP,链表结构

    前言

    我靠网上怎么全是带 (log) 的做法,看题目限制应该是放了一支 (log),但是由于我太菜没想出来,所以来写 (mathcal O(n)) 的题解了。

    似乎常数比较小。

    题意

    不做赘述,注意给定的序列 ({p}) 其实是类似于后缀数组中的 sa 数组,不是每个下标的排名。

    思路

    子串匹配问题,输出方案,哇这限制条件,这好 (mathcal O(n)) 啊,而且是逐字匹配,感觉很像 KMP。

    我们就先向 KMP 方向考虑,注意到输入序列 ({p}) 是以排名为下标,没有意义,转换成每个下标的排名数组,注意此时还是一个排列。

    我们考虑一个成功的匹配是怎样的,即序列 ({a}) 的某一个子串离散化排序后对应的下标是和序列 ({p}) 完全一致的。

    那我们如何重新定义这个 “匹配” 和 “一致”,如何在 KMP 建立正确的 next 数组中进行最优的比较操作?

    在建立 next 的数组的过程中,当我们考虑到下标 (i) 时,我们假设已经像普通的 KMP 思路已经继承了 (i - 1) 的最优匹配,我们比较两个 (i)(next[i] + 1) (在比较序列 (p[1,dots,next[i - 1] + 1])(p[i - next[i - 1], dots, i]) 的意义下)是否相等,即比较 (p[next[i - 1] + 1]) 在其前缀序列的排名和 (p[i]) 在其后缀序列的排名是否相等,否则一直跳 next,正确性显然是对的。

    到这里,是可以用树状数组做了。

    对于 (mathcal O(n)) 的做法,我们可以更好地利用一个性质:已匹配的两个字串排名是完全相等的,所以我们只需要比较一下 (i) 的前驱和后继与 (next[i - 1] + 1) 的前驱和后继(相对于分别的匹配子串的)位置是否相等,这样同样保证了两者在匹配子串的相对位置。

    而我们只需要求得模式串的前驱和后继即可,将其拿来比较即可。

    (这里的前驱和后继是指值小于/大于某个数,且下标小于某个数)

    代码

    -O2 854ms 目前最优解。

    int a, b, s[2][N + 5], L[N + 5], R[N + 5], nxt[N + 5], p[N + 5], l[N + 5], r[N + 5];
    vector<int> ans;
    
    bool check(int o, int i, int j, int mn, int mx) {
    	int res = 1;
    	if (mn) res &= (s[o][i - (j - mn)] < s[o][i]);
    	if (mx) res &= (s[o][i - (j - mx)] > s[o][i]);
    	return res;
    }
    
    void init() {
    	rep(i, 1, a) l[i] = p[s[0][i] - 1], r[i] = p[s[0][i] + 1];
    	per(i, a, 1) {
    		L[i] = l[i];
    		R[i] = r[i];
    		l[r[i]] = l[i];
    		r[l[i]] = r[i];
    	}
    	int j = 0;
    	rep(i, 2, a) {
    		while (j && !check(0, i, j + 1, L[j + 1], R[j + 1])) j = nxt[j];
    		if (check(0, i, j + 1, L[j + 1], R[j + 1])) ++j;
    		nxt[i] = j;
    	}
    }
    
    signed main() {
    	// freopen("in1.in", "r", stdin);
    	// freopen("out.out", "w", stdout);
    	a = read();
    	b = read();
    	rep(i, 1, a) p[i] = read();
    	rep(i, 1, a) s[0][p[i]] = i;
    	rep(i, 1, b) s[1][i] = read();
    	init();
    	int j = 0;
    	rep(i, 1, b) {
    		while (j && !check(1, i, j + 1, L[j + 1], R[j + 1])) j = nxt[j];
    		if (check(1, i, j + 1, L[j + 1], R[j + 1])) ++j;
    		if (j == a) ans.PB(i - a + 1), j = nxt[j];
    	}
    	printf("%d
    ", siz(ans));
    	rep(i, 0, siz(ans) - 1) printf("%d ", ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    (二)建筑物多边形化简系列——多边形点数化简
    (一)建筑物多边形化简系列——去除噪点环
    (三)建筑物多边形化简系列——去除冗余点
    (五)建筑物多边形化简系列——最小外接矩形的获取
    vue笔记
    学习react基础知识(五)
    学习react基础知识(四)
    学习react基础知识(三)
    学习react基础知识(二)
    学习react基础知识(一)
  • 原文地址:https://www.cnblogs.com/RedreamMer/p/14813788.html
Copyright © 2020-2023  润新知