zoukankan      html  css  js  c++  java
  • 【CF1550E】Stringforces

    题目

    题目链接:http://codeforces.com/problemset/problem/1550/E
    (s) 是一个由前 (k) 个小写字母构成的字符串,(v) 是前 (k) 个小写字母中的某一个。定义 (mathrm{MaxLen}(s,v)) 表示 (s) 所有仅由字母 (v) 构成的连续子串的最长长度。
    定义 (s) 的价值为所有 (mathrm{MaxLen}(S,v)) 的最小值,其中 (v) 取遍前 (k) 个小写字母。
    现在给定一个长度为 (n) 的字符串 (s)(s) 中字母要么是前 (k) 个小写字母中的某一个,要么是问号。你需要将 (s) 中的每一个问号替换成前 (k) 个小写字母中的一个,并最大化 (s) 的价值。方便起见,你只需要输出这个最大的价值即可。
    (1leq nleq 2 imes 10^5)(1leq kleq 17)

    思路

    这个 (kleq 17) 很明显在提示状压。
    二分答案,设 (f[i]) 表示状态 (i) 里面所有字符都已经至少存在一个长度为 (mid) 的子串,最少需要覆盖掉 (s) 前多少个字符。
    预处理出 ( ext{nxt}[i][j]) 表示从字符串第 (i) 个字符起,要塞下连续 (mid) 个字符 (j),末尾最小会到达哪里。转移时考虑刷表,枚举不在集合 (i) 中的一个元素 (j),那么

    [f'[icup j]=min(f[icup j], ext{nxt}[f[i]+1][j]) ]

    最后判断一下 (f[2^m-1]) 是否 (leq n) 即可。
    时间复杂度 (O((nk+2^kk)log n))

    代码

    /*
    #pragma GCC optimize("Ofast")
    #pragma GCC optimize("unroll-loops")
    #pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,tune=native")
    */
    #include <bits/stdc++.h>
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    using namespace std;
    typedef long long ll;
    typedef long double ld;
    typedef unsigned long long ull;
    
    const int N=200010,M=19;
    int n,m,lim,cnt[N][M],nxt[N][M],f[1<<M];
    char s[N];
    
    int main()
    {
    	scanf("%d%d%s",&n,&m,s+1);
    	for (int i=1;i<=n;i++)
    		for (int j=1;j<=m;j++)
    			cnt[i][j]=cnt[i-1][j]+(s[i]-'a'+1==j);
    	int l=0,r=n/m,mid;
    	while (l<=r)
    	{
    		memset(f,0x3f3f3f3f,sizeof(f));
    		memset(nxt,0x3f3f3f3f,sizeof(nxt));
    		f[0]=0; lim=(1<<m);
    		mid=(l+r)>>1;
    		for (int i=n-mid+1;i>=1;i--)
    		{
    			int c=0;
    			for (int j=1;j<=m;j++)
    				if (cnt[i+mid-1][j]-cnt[i-1][j])
    				{
    					if (c) { c=-1; break; }
    					c=j;
    				}
    			for (int j=1;j<=m;j++)
    				if (!c || c==j) nxt[i][j]=i+mid-1;
    					else nxt[i][j]=nxt[i+1][j];
    		}
    		for (int i=0;i<lim;i++)
    			for (int j=1;j<=m;j++)
    				if (!(i&(1<<j-1)) && f[i]<=n && nxt[f[i]+1][j]<=n)
    					f[i|(1<<j-1)]=min(f[i|(1<<j-1)],nxt[f[i]+1][j]);
    		if (f[lim-1]<=n) l=mid+1;	
    			else r=mid-1;
    	}
    	cout<<l-1;
    	return 0;
    }
    
  • 相关阅读:
    我的暑假周记2018.7.21
    大道至简读后感
    我的暑假周记2018.7.15
    继承与多态
    java联级调用
    古罗马凯撒大帝字串加密
    作业三
    线性同余法产生1000个随机数
    Text2
    java登录界面
  • 原文地址:https://www.cnblogs.com/stoorz/p/15237441.html
Copyright © 2011-2022 走看看