zoukankan      html  css  js  c++  java
  • @atcoder


    @description@

    给定由若干长度 <= N 的 01 字符串组成的集合 S。请找到长度最长的串 t(如果有多个选字典序最小的),使得存在 >= K 个 S 中的字符串,使得 t 是这些字符串的子序列。

    原题题面。

    @solution@

    先看看怎么检验串 t 是否为某个串 s 的子序列:从前往后匹配,贪心地找最前面一个能够匹配上的。

    注意到匹配的过程可以建图:每个种类的串 s 建点,向第一次出现的 0/1 对应的后缀连边。
    每个点的连边是 O(1),因此假如把所有 <= N 的串建这个图,实际得到的图也不会很大。

    于是就有一个思路:枚举 t,每次将 S 中的串对应的点在这个图上进行移动,看剩余的点是否依然 >= K 个。
    看似会 TLE,然而可以修正一下:如果两个字符串走到了同一个点,下一次只移动这一个点即可。

    看似还会 TLE,实际上可以过了。
    因为每个长度为 p 的字符串会有 2^p 种可能性,而它继续往下匹配只会剩下 2^(N-p) 种匹配可能。因此每一个 p 都是 O(2^N) 的复杂度。
    因此总复杂度为 O(N*2^N) (应该是吧,没有认真算过)。

    @accepted code@

    #include <cstdio>
    
    int ch[2][1<<22], id[22][1<<21], cnt;
    void get() {
    	id[0][0] = (cnt++), ch[0][id[0][0]] = ch[1][id[0][0]] = -1;
    	for(int i=1;i<=20;i++) {
    		int t = (1 << i), k = (t >> 1);
    		for(int s=0;s<t;s++) {
    			id[i][s] = (cnt++);
    			int p = (s & k), q = (p ? 1 : 0);
    			ch[q][id[i][s]] = id[i-1][s^p];
    			ch[!q][id[i][s]] = ch[!q][id[i-1][s^p]];
    		}
    	}
    }
    
    int a[22][1<<22], siz[22], c[22][1<<22], num[22][1<<22];
    
    int ans[22], N, K;
    void dfs(int d, int s) {
    	if( ans[d] == -1 ) ans[d] = s;
    	for(int p=0;p<=1;p++) {
    		int tot = 0;
    		for(int i=0;i<siz[d];i++) {
    			int to = ch[p][a[d][i]];
    			if( to == -1 ) continue;
    			if( num[d + 1][to] == -1 )
    				num[d + 1][a[d + 1][siz[d + 1]] = to] = siz[d + 1], siz[d + 1]++;
    			tot += c[d][i], c[d + 1][num[d + 1][to]] += c[d][i];
    		}
    		if( tot >= K ) dfs(d + 1, (s << 1) | p);
    		for(int i=0;i<siz[d + 1];i++)
    			num[d + 1][a[d + 1][i]] = -1, c[d + 1][i] = 0;
    		siz[d + 1] = 0;
    	}
    }
    
    char s[1<<21];
    int main() {
    	scanf("%d%d", &N, &K), get();
    	for(int i=0;i<=N;i++)
    		for(int j=0;j<cnt;j++)
    			num[i][j] = -1;
    	for(int i=0;i<=N;i++) {
    		scanf("%s", s);
    		int t = (1 << i);
    		for(int j=0;j<t;j++) {
    			if( s[j] == '1' )
    				num[0][a[0][siz[0]] = id[i][j]] = siz[0], c[0][num[0][id[i][j]]]++, siz[0]++;
    		}
    		ans[i] = -1;
    	}
    	dfs(0, 0);
    	for(int i=N;i>=0;i--) {
    		if( ans[i] != -1 ) {
    			for(int j=i-1;j>=0;j--)
    				putchar(((ans[i] >> j) & 1) + '0');
    			puts(""); return 0;
    		}
    	}
    }
    

    @details@

    事实证明再怎么精打细算还是有想象之外的越界危险。

    还不如直接数组开大 2 倍。

  • 相关阅读:
    [ jquery 选择器 :hidden ] 此方法选取匹配所有不可见元素,或者type为hidden的元素
    剑指 Offer 03. 数组中重复的数字 哈希
    LeetCode 1736. 替换隐藏数字得到的最晚时间 贪心
    Leetcode 1552. 两球之间的磁力 二分
    Leetcode 88. 合并两个有序数组 双指针
    LeetCode 1744. 你能在你最喜欢的那天吃到你最喜欢的糖果吗?
    LeetCode 1743. 相邻元素对还原数组 哈希
    LeetCode 1745. 回文串分割 IV dp
    剑指 Offer 47. 礼物的最大价值 dp
    剑指 Offer 33. 二叉搜索树的后序遍历序列 树的遍历
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12211035.html
Copyright © 2011-2022 走看看