题意:
定义一个合法序列是形如:
的字符串,其中 \(x_i\) 为用逗号分隔的若干不含前导零的严格递增的正整数(可以不止一位)
现在给你一个字符串,有些位置是已经确定是一个逗号或者一个数字,还有一些位置未知,用问号表示。还原这个字符串使其成为一个合法序列。如果无解,输出 impossible;如果多解,输出字典序最小的合法序列(即输出 \(x_1\) 最小的,如果还有多种合法序列,输出 \(x_2\) 最小的,以此类推...)
\(|S| \le 500\). 多测
分析:
这题还是很妙的
既然要求字典序最小,容易想到从前往后贪心构造答案。考虑已经确定了 \(x_1,x_2,...,x_k\),我们现在枚举最小的 \(x_{k+1}\) 使得其大于 \(x_k\) 的同时满足填 \(x_{k+1}\) 后剩下的部分依旧有合法解。
考虑判断 \(x_{k+1}\) 大于 \(x_k\) 是较为轻松的。问题是判断填 \(x_{k+1}\) 后剩下的部分是否有合法解。即判断原字符串的后缀能否还原成合法序列。如果能,其 \(x_1\) 的值最大是多少(因为还要满足这个值大于我们枚举的 \(x_{k+1}\) )。因为数据范围很小我们考虑 dp 解决这个问题。设 \(f_i\) 是原串的 \(i\sim n\) 这个后缀,还原成合法序列后,第一个数字最大是多少。容易发现 \(f\) 是一个字符串类型而不是一个数字。
考虑计算 \(f_i\). 容易想到枚举第一个数字在 \(i\sim n\) 中所占位置 \([i,j]\),然后 \(j+1\) 处填逗号,\(j+2\) 处填第二个数字。因此需要计算 \([i,j]\) 部分在满足小于 \(f_{j+2}\) 的情况下最大的可取数字。但是要保证 \([i,j]\) 之间没有已经给定的逗号。同时特判 \(j=n\) 的边界情况。
设 \(maxconvert(l,r,cmp)\) 是 \(s[l...r]\) 能填出的最大的数满足其小于数字 \(cmp\). (就是把上文中的计算写在了一个函数中)。容易发现,当 \(r-l+1 > |cmp|\) 的时候一定无解;类似地当 \(r-l+1 < |cmp|\) 的时候所有 ? 填 9 即可。难写的是 \(r-l+1 = |cmp|\) 的情况。即两个串长度相等。
我们要让构造出来的数“逼近” \(cmp\),所以从前往后填,如果当前位能做到和 \(cmp\) 中的对应位相等,就让它们相等。记录一个最大的 \(pos\). 满足构造出的 \(ret\) 和 \(cmp\) 前 \(pos\) 位可以做到相等,然后 \(ret\) 的 \(pos\) 位是第 \(cmp\) 的 \(pos\) 位减 \(1\). 之后的位能填 \(9\) 就填 \(9\).
显然当我们枚举第 \(k\) 位试图让它们相等的时候,如果 \(ret\) 的第 \(k\) 位已经被钦定,讨论和 \(cmp\) 的第 \(k\) 位大小关系即可(如果大于或者小于就退出停止比较);如果第 \(k\) 位是?,我们判断第 \(k\) 位能否填一个小于 \(cmp\) 的第 \(k\) 位的数从而让 \(pos\) 变大。那么如果 \(cmp\) 中这一位为 \(0\) 我们也只能跟着填 \(0\). 否则我们就可以填 \(cmp_k-1\) 数码。但是注意,由于没有前导零,所以如果 \(k=1,cmp_k=1\),我们也只能跟着填 \(1\).
用 \(maxconvert\) 计算完 \(f\) 后我们就可以构造答案了。 类似地枚举 \(a_{k+1}\) 所占位置 \([i,j]\),设 \(minconvert(l,r,cmp_1,cmp_2)\) 为 \([i,j]\) 能填出的最小数字满足其小于 \(cmp_1=f_{j+2}\) 的同时大于 \(cmp_2=a_k\). 注意到位数越小就越优秀,所以从小往大枚举 \(j\),当一个 \([i,j]\) 合法,我们就直接令 \(a_{k+1}=minconvert(i,j,cmp_1,cmp_2)\). 然后更新 \(cmp2\).
至于 \(minconvert\) 函数的计算是类似的。我们不考虑小于 \(cmp_1\) 的限制,构造一个最小的大于 \(cmp_2\) 的串最后判断其是否小于 \(cmp_1\) 即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<vector>
#define rep(i,a,b) for(ll i=(a);i<=(b);i++)
#define per(i,a,b) for(ll i=(a);i>=(b);i--)
#define op(x) ((x&1)?x+1:x-1)
#define odd(x) (x&1)
#define even(x) (!odd(x))
#define lc(x) (x<<1)
#define rc(x) (lc(x)|1)
#define lowbit(x) (x&-x)
#define Max(a,b) (a>b?a:b)
#define Min(a,b) (a<b?a:b)
#define next Cry_For_theMoon
#define il inline
#define pb(x) push_back(x)
#define is(x) insert(x)
#define sit set<int>::iterator
#define mapit map<int,int>::iterator
#define pi pair<int,int>
#define ppi pair<int,pi>
#define pp pair<pi,pi>
#define fr first
#define se second
#define vit vector<int>::iterator
#define mp(x,y) make_pair(x,y)
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;
typedef double db;
using namespace std;
const int MAXN=520;
char str[MAXN],ans[MAXN];
int n;
struct Node{
char str[MAXN];
int len;
void reset(){len=0;memset(str,0,sizeof str);}
int operator<(const Node& n2)const{
if(len<n2.len)return 1;
if(len>n2.len)return 0;
rep(i,1,len)if(str[i]<n2.str[i])return 1;else if(str[i]>n2.str[i])return 0;
return 0;
}
Node operator=(const Node& n2){
this->reset();
len=n2.len;
rep(i,1,len)str[i]=n2.str[i];
return *this;
}
}f[MAXN],maxn; //maxn记录构造到的最后一个数
void maxconvert(int l,int r,const Node& g){
Node tmp;tmp.reset();
int len=r-l+1;
if(r+2<=n && len>g.len)return;
if(len<g.len || r+2>n){
//直接填9999
rep(i,l,r){
tmp.str[++tmp.len]=(str[i]=='?')?'9':str[i];
}
if(tmp.str[1]!='0')f[l]=tmp;
return;
}
int pos=-1; //最大的可能的二者不同的位置
rep(i,1,len){
if(str[l+i-1]=='?' && str[l+i-1]==g.str[i])continue;
if(str[l+i-1]!='?' && str[l+i-1]<g.str[i]){pos=i;break;}
if(str[l+i-1]!='?' && str[l+i-1]>g.str[i]){break;}
if(g.str[i]=='0')continue; //必须跟着填0
if(g.str[i]=='1' && i==1)continue; //第一位是1则必须跟着填1
pos=i; //可以做到在第i位不同
}
if(pos==-1)return;
//构造答案
rep(i,1,pos-1){tmp.str[++tmp.len]=g.str[i];}
if(str[l+pos-1]=='?')tmp.str[++tmp.len]=g.str[pos]-1;
else tmp.str[++tmp.len]=str[l+pos-1];
rep(i,pos+1,len){tmp.str[++tmp.len]=(str[l+i-1]=='?'?'9':str[l+i-1]);}
if(tmp.str[1]!='0')f[l]=tmp;
}
int minconvert(int l,int r,const Node& g1,const Node& g2){
Node tmp;tmp.reset();
int len=r-l+1;
if(len<g1.len)return 0;
if(len>g1.len){
//填1000
tmp.str[++tmp.len]=(str[l]=='?')?'1':str[l];
rep(i,l+1,r)tmp.str[++tmp.len]=(str[i]=='?')?'0':str[i];
if(tmp.str[0]!='0' && (tmp<g2 || r+1>n)){
rep(i,l,r)ans[i]=tmp.str[i-l+1];
maxn=tmp;
return 1;
}
return 0;
}
int pos=-1;
rep(i,1,len){
if(str[l+i-1]!='?' && str[l+i-1]==g1.str[i])continue;
if(str[l+i-1]!='?' && str[l+i-1]>g1.str[i]){pos=i;break;}
if(str[l+i-1]!='?' && str[l+i-1]<g1.str[i]){break;}
if(g1.str[i]=='9')continue; //必须跟着填9
pos=i;
}
if(pos==-1)return 0;
rep(i,1,pos-1)tmp.str[++tmp.len]=g1.str[i];
if(str[l+pos-1]=='?')tmp.str[++tmp.len]=g1.str[pos]+1;
else tmp.str[++tmp.len]=str[l+pos-1];
rep(i,pos+1,len)tmp.str[++tmp.len]=(str[l+i-1]=='?')?'0':str[l+i-1];
if(tmp.str[0]!='0' && (tmp<g2 || r+1>n)){
rep(i,l,r)ans[i]=tmp.str[i-l+1];
maxn=tmp;
return 1;
}
return 0;
}
int main(){
while(cin>>(str+1)){
n=strlen(str+1);
memset(ans,0,sizeof ans);
rep(i,0,n+10)f[i].reset();
maxn.reset();
per(i,n,1){
if(str[i]==',')continue;
rep(j,i,n){
//[i,j]为一段,j+1为',',j+2为新的数
if(str[j]==',')break; //不能跨越','
if((j==n) || (str[j+1]==',') || (str[j+1]=='?' && j+2<=n && str[j+2]!=',')){
maxconvert(i,j,f[j+2]);
}
}
}
if(f[1].len==0){
printf("impossible\n");
continue;
}
/*
rep(i,1,n){
printf("f[%d]\n",i);
printf("%s\n",f[i].str+1);
}
*/
int succ=1;
rep(i,1,n){
if(ans[i]==',')continue;
int flag=0;
rep(j,i,n){
if(str[j]==',')break;
if((j==n) || (str[j+1]==',') || (str[j+1]=='?' && j+2<=n && str[j+2]!=',')){
if(minconvert(i,j,maxn,f[j+2])){ //显然如果位数少可以就不考虑j更大了
if(j+1<=n)ans[j+1]=',';
flag=1;
// printf("[%d,%d]\n",i,j);
i=j;break;
}
}
}
if(!flag){
succ=0;break;
}
}
if(!succ)printf("impossible\n");
else printf("%s\n",ans+1);
}
return 0;
}
/*
??3,?,4??3,40?,??50
之前code被这组hack了qwq
*/

浙公网安备 33010602011771号