[CF1550E]Stringforces
壹、题目描述 ¶
贰、题解 ¶
真的是一道妙妙题。 关键在于二分答案之后利用贪心,在 \(\rm DP\) 上进行转移。
首先考虑二分答案 \(d\),进行检测时,由于 \({\Large|}\Sigma{\Large|}\) 很小,我们考虑状压,那么一个很显然的一维状态就是 \(f(s)\) 表示当前字符满足情况为 \(s\),但是值里面应该记录什么呢?我们不妨挖掘题目性质:
这个题目显然是存在贪心思路的,先不在意那些已经有东西的位置,我们显然是很想让填完了我们的目标字符集后,剩下的位置尽量多 —— 剩得多,才有无限可能嘛,也即,尽可能让我们填进去的字符占的位置尽量靠左(下标尽量小)。
那么,现在状态完整了,\(f(s)\) 表示填完目标字符集后的右端点最小是多少,如果 \(f(U)\le n\),那么当前二分答案合法。
考虑如何进行转移,显然我们如果要填一个字符,肯定都是连着一次性填完,那么,对于一个位置 \(i\),如果 \([i,i+d-1]\) 中除了我们想要填的字符 \(c\) 以及空格以外没有别的地方,那么我们就可以将字符 \(c\) 填入,那么如何快速判断?
考虑定义状态 \(g(i,c)\) 表示自位置 \(i\) 以后,第一个 能放下 \(c\) 的位置的右端点是多少,转移其实很简单,从后往前考虑一个位置 \(i\),如果 \([i,i+d-1]\) 没有别的字符(这一个问题我们可以通过存储每个字符最后一次出现的位置在哪里来解决),那么 \(g(i,c)=i\),否则 \(g(i,c)=g(i+1,c)\).
预处理 \(\mathcal O(nk)\),而 \(\rm DP\) 部分 \(\mathcal O(2^k)\),总复杂度即为 \(\mathcal O(nk\log n+2^k\log n)\).
叁、参考代码 ¶
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
namespace Elaina{
#define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
#define drep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define mmset(a, b) memset(a, b, sizeof a)
// #define int long long
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
template<class T>inline T fab(T x){ return x<0? -x: x; }
template<class T>inline void getmin(T& x, const T rhs){ x=min(x, rhs); }
template<class T>inline void getmax(T& x, const T rhs){ x=max(x, rhs); }
template<class T>inline T readin(T x){
x=0; int f=0; char c;
while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
return f? -x: x;
}
template<class T>inline void writc(T x, char s='\n'){
static int fwri_sta[1005], fwri_ed=0;
if(x<0) putchar('-'), x=-x;
do fwri_sta[++fwri_ed]=x%10, x/=10; while(x);
while(putchar(fwri_sta[fwri_ed--]^48), fwri_ed);
putchar(s);
}
}
using namespace Elaina;
const int maxn=2e5;
const int maxk=17;
char s[maxn+5];
int n, k, U;
inline void input(){
n=readin(1), k=readin(1), U=1<<k;
scanf("%s", s);
}
inline bool check(int d){
vector<int>lst(k, n);
vector< vector<int> >pos(n+1, vector<int>(k, n+1));
for(int i=n-1; i>=0; --i){
if(s[i]!='?') lst[s[i]-'a']=i;
int cur=n;
for(int j=0; j<k; ++j){
if(i+d>cur) pos[i][j]=pos[i+1][j];
else pos[i][j]=i+d;
cur=min(cur, lst[j]);
}
cur=n;
for(int j=k-1; j>=0; --j){
if(i+d>cur) pos[i][j]=pos[i+1][j];
cur=min(cur, lst[j]);
}
}
vector<int>f(U, n+1);
f[0]=0;
for(int i=0; i<U; ++i) if(f[i]<n+1){
for(int j=0; j<k; ++j) if(!((i>>j)&1))
getmin(f[i^(1<<j)], pos[f[i]][j]);
}
return f[U-1]<=n;
}
signed main(){
input();
int l=0, r=n, mid, ans=0;
while(l<=r){
mid=(l+r)>>1;
if(check(mid)) ans=mid, l=mid+1;
else r=mid-1;
}
writc(ans);
return 0;
}
肆、关键之处 ¶
设计 \(\rm DP\) 状态时应当考虑最佳转移策略,根据这个策略我们想出方法进行维护。
原文地址:https://www.cnblogs.com/Arextre/p/15043501.html
- TCP/IP, WebSocket 和 MQTT
- struts2: 玩转 rest-plugin
- 设置系统环境变量立即生效的VBS脚本
- velocity模板引擎学习(1)
- mybatis 3.x 缓存Cache的使用
- XStream、JAXB 日期(Date)、数字(Number)格式化输出xml
- mac: vmware fusion中cent os启动假死的解决办法
- java:hibernate + oracle之坑爹的clob
- 启用WCF NetTcpBinding的共享端口
- asp中的md5/sha1/sha256算法收集
- UE4从零搭建CF游戏关卡(蓝图篇)
- 通用的序列号生成器库
- 利用Geneva开发SOA的安全模型
- STOMP协议介绍
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- String - 12. Integer to Roman
- Dynamic Programming - 62. Unique Paths
- Dynamic Programming - 70. Climbing Stairs
- LinkedList - 23. Merge k Sorted Lists
- LinkedList - 86. Partition List
- LinkedList - 148. Sort List
- LinkedList - 61. Rotate List
- LinkedList - 143. Reorder List
- LinkedList - 160. Intersection of Two Linked Lists
- LinkedList - 21. Merge Two Sorted Lists
- LinkedList - 82. Remove Duplicates from Sorted List II
- LinkedList - 203. Remove Linked List Elements
- LinkedList - 83. Remove Duplicates from Sorted List
- LinkedList - 19. Remove Nth Node From End of List
- LinkedList - 92. Reverse Linked List II