• 【SHTSC2013】阶乘字符串


    题意

    判断前(n)个小写字母的全排列是否都在给定字符串(S)中作为子序列出现过。
    (nleq 26,|S| leq 450)

    解析

    根据dalao的精确计算,当(n>21)时,(C_{450}^{n}<n!),所以(n>21)的情况可以直接输出"NO"。

    对于一般的情况,设(f_s)表示字符串(S)中的一个位置,使得集合(s)中的字母的全排列都在([1,f(s)])中出现过。此外还需要一个辅助数组(next[i][j]),表示字符串(S)中第(i)个位置后第一个字母(j)出现的位置(包括(i))。那么我们只需要枚举集合(s)的全排列是以哪个字母结尾的,就能转移了。

    [f_s=max_{i in s}(next[f[s-bit(i)]][i]) ]

    时间复杂度(O(n*2^n)),有了前面(nleq 21)的保证,就可以顺利通过了。

    Code

    #include <cstdio>
    #include <cstring>
    
    const int N = 507;
    int min(int a, int b) { return a < b ? a : b; }
    int max(int a, int b) { return a > b ? a : b; }
    
    int T, n, len, f[1 << 21];
    int next[N][26];
    char str[N];
    
    int main()
    {
    	scanf("%d", &T);
    	while (T--)
    	{
    		memset(next, 0x3f, sizeof(next));
    		memset(f, 0, sizeof(f));
    		scanf("%d", &n);
    		scanf("%s", str + 1);
    		if (n > 21) { printf("NO
    "); continue; }
    		len = strlen(str + 1);
    		for (int i = 1; i <= len; i++)
    			for (int j = 0; j < n; j++)
    				if (j == str[i] - 'a') next[i][j] = i;
    				else next[i][j] = len + 1;
    		for (int i = len; i >= 1; i--) for (int j = 0; j < n; j++) next[i][j] = min(next[i][j], next[i + 1][j]);
    		int full = (1 << n) - 1;
    		f[0] = 1;
    		for (int s = 1; s <= full; s++)
    			for (int i = 0; i < n; i++)
    				if ((1 << i) & s)
    				{
    					if (next[f[s - (1 << i)]][i] >= 0x3f3f3f3f) f[s] = len + 1;
    					else f[s] = max(f[s], next[f[s - (1 << i)]][i]);
    				}
    		if (f[full] <= len) printf("YES
    ");
    		else printf("NO
    ");
    	}
    	return 0;
    }
    
  • 相关阅读:
    高位前缀和,求他的子集的和https://ac.nowcoder.com/acm/contest/4784/A
    Codeforces Global Round 7 E. Bombs
    高精度,乘法加法
    2018-ICPC-焦作区预赛
    状压dp,区间dp,矩阵快速幂
    树状数组,适用于单点修改,区间查询
    离散化函数
    带修莫队模版
    树链剖分 https://www.luogu.com.cn/problem/P3384
    HDU 1016 Prime Ring Problem【DFS】
  • 原文地址:https://www.cnblogs.com/zjlcnblogs/p/11121421.html
Copyright © 2020-2023  润新知