zoukankan      html  css  js  c++  java
  • @loj


    @description@

    对于整数序列 ((a_1, a_2, ..., a_n)) 和 1 ~ n 的排列 ((p_1, p_2, ..., p_n)),称 ((a_1, a_2, ..., a_n)) 符合 ((p_1, p_2, ..., p_n)),当且仅当:

    (1){a} 中任意两个数字互不相同。
    (2)将 ((a_1, a_2, ..., a_n)) 从小到大排序后,将会得到 ((a_{p_1}, a_{p_2}, ..., a_{p_n}))

    现在给出 1 ~ n 的排列 {p} 与序列 (h_1, h_2, ..., h_m),请你求出哪些 h 的子串符合排列 {p}。

    输入格式
    第一行两个空格隔开的正整数 n, m。
    第二行 n 个空格隔开的正整数,表示排列 p。
    第三行 m 个空格隔开的正整数,表示序列 h。

    输出格式
    第一行一个整数 k,表示符合 {p} 的子串个数。
    第二行 k 个空格隔开的正整数,表示这些子串的起始位置(编号从 1 开始)。请将这些位置按照从小到大的顺序输出。特别地,若 k = 0,那么你也应当输出一个空行。

    样例输入
    5 10
    2 1 5 3 4
    5 6 3 8 12 7 1 10 11 9
    样例输出
    2
    2 6

    数据范围与提示
    2 <= n <= m <= 1000000; 1 <= hi <= 10^9; 1 <= pi <= n。
    且保证 {h} 中元素互不相同,{p} 是一个排列。

    @solution@

    先对问题作一步转化:求 {q} 使得 (q_{p_i} = i),即 {p} 的逆置换。
    那么某个子串符合 {p} 可以等价于这个子串离散化到 1 ~ n 中后等于 q。

    如果不考虑离散化,那么就是一个经典子串匹配问题,直接上 kmp。
    假如子串同构的判定方法如上,即离散化后同构,是否还可以扩展一下 kmp 呢?

    考虑 kmp 什么时候需要判同构:已知串 s 与串 t 同构时,在 s 末尾加一个 a,在 t 末尾加一个 b,判断 s + a 与 t + b 是否同构。
    因为 s 与 t 已经同构了,只需要 a 与 b 加入进去过后仍然同构即可。
    可以等价于判定 a 在 s 中的排名(s 中比 a 小的数) = b 在 t 中的排名(t 中比 b 小的数)。

    查排名可以平衡树,不过这道题直接离散化 + 树状数组即可。
    注意 kmp 在跳 fail 的时候,需要一个个元素的移动,因为要维护树状数组。
    不过复杂度的证明是不会变的。kmp 还是 O(n),套个树状数组就是 O(nlogn) 的。

    @accepted code@

    #include <cstdio>
    #include <vector>
    #include <algorithm>
    using namespace std;
    const int MAXN = 1000000;
    int n, m;
    int t[2][MAXN + 5];
    int lowbit(int x) {return x & -x;}
    void update(int x, int k, int type) {
    	for(int i=x;i<=m;i+=lowbit(i))
    		t[type][i] += k;
    }
    int sum(int x, int type) {
    	int ret = 0;
    	for(int i=x;i;i-=lowbit(i))
    		ret += t[type][i];
    	return ret;
    }
    int d[MAXN + 5], p[MAXN + 5], h[MAXN + 5];
    void discrete() {
    	for(int i=1;i<=m;i++) d[i] = h[i];
    	sort(d + 1, d + m + 1);
    	for(int i=1;i<=m;i++)
    		h[i] = lower_bound(d + 1, d + m + 1, h[i]) - d;
    }
    int f[MAXN + 5];
    void get_f() {
    	f[1] = 0;
    	int ri = 0, le = 2;
    	for(int i=2;i<=n;i++) {
    		int j = f[i-1];
    		while( sum(p[j+1], 0) != sum(p[i], 1) ) {
    			while( ri != f[j] )
    				update(p[ri--], -1, 0), update(p[le++], -1, 1);
    			j = f[j];
    		}
    		f[i] = j + 1;
    		update(p[++ri], 1, 0), update(p[i], 1, 1);
    	}
    }
    vector<int>ans;
    void get_ans() {
    	for(int i=1;i<=m;i++)
    		t[0][i] = t[1][i] = 0;
    	int le = 1, ri = 0, j = 0;
    	for(int i=1;i<=m;i++) {
    		while( sum(p[j+1], 0) != sum(h[i], 1) ) {
    			while( ri != f[j] )
    				update(p[ri--], -1, 0), update(h[le++], -1, 1);
    			j = f[j];
    		}
    		j++;
    		update(p[++ri], 1, 0), update(h[i], 1, 1);
    		if( j == n ) {
    			ans.push_back(i-n+1);
    			while( ri != f[j] )
    				update(p[ri--], -1, 0), update(h[le++], -1, 1);
    			j = f[j];
    		}
    	}
    }
    int main() {
    	scanf("%d%d", &n, &m);
    	for(int i=1;i<=n;i++) {
    		int x; scanf("%d", &x);
    		p[x] = i;
    	}
    	for(int i=1;i<=m;i++) scanf("%d", &h[i]);
    	discrete(), get_f(), get_ans();
    	printf("%d
    ", (int)ans.size());
    	for(int i=0;i<(int)ans.size();i++)
    		printf("%d%c", ans[i], (i + 1 == ans.size() ? '
    ' : ' '));
    	if( ans.empty() ) puts("");
    }
    

    @details@

    曾经想过字符串 hash,因为这个 hash 直接就是康托展开算。
    发现这个题 hash 并不能前缀和相减,动态维护感觉还要写平衡树,于是放弃了。
    毕竟相对于平衡树大家还是喜欢代码简短的树状数组吧。

  • 相关阅读:
    struts2配置文件详解
    实体类和数据库映射--配置文件
    Hibernate常用配置文件详解
    本地计算机上的OracleOraDb11g_home1TNSListener服务启动后停止。某些服务在未由其他服务或程序使用时将自动停止。——Oracle监听器服务无法启动!
    关于远程访问Oracle数据库的设置(共享数据库)
    Log4J日志配置详解
    Java compiler level does not match the version of the installed Java project facet.问题
    Genymotion出现Unable to load VirtualBox engine问题--100%解决
    eclipse注解——作者,创建时间,版本
    web工程下的html中引用其他目录下的文件的path
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11845570.html
Copyright © 2011-2022 走看看