题意:
给你一个包含’‘、’?’、’!’、’a’~’z’的字符串,其中’‘可以被任意字符串替换(包括空串),’?’必须恰好被一个字符替换,’!’必须恰好被三个字符替换。要求按规则替换后原串要变成一个回文串且长度要求最小;若长度相同则要求字典序最小。
tip:
第一次做的时候按照dp[i][j]表示前I个和后j个能否匹配,匹配的最小长度是多少。松弛:
void rel(int i,int j,int i2,int j2,int lenth){
if(dp[i2][j2].first == false){
return ;
}
dp[i][j].first = true;
if(dp[i2][j2].second + lenth < dp[i][j].second){
dp[i][j].second = dp[i2][j2].second + lenth;
}
}
当s[i]或者s[j]是的时候,要枚举一个k,表示右边k个和左边※之前的匹配。。。长度要减去j到k之前是的。因为后面的*是空字符才是最优,不然现在左边这个星还要匹配他
for(int i = 1 ; i <= n ; i++){
for(int j = 1 ; j <= n ; j++){
int l = i-1, r = n-j;
if(is_low(p[l]) && is_low(p[r])){
if(p[l] != p[r]){
dp[i][j].first = false;
}
else{
rel(i,j,i-1,j-1,1);
}
}
else if( p[r] == '?' || p[l] == '?'){
rel(i,j,i-1,j-1,1);
}
else if(p[l] == '*' ){
for(int k = 0; k <= j ; k++){
r2 = n-k;
rel(i,j,i-1,k,r2-r-(cnt[r2-1]-cnt[r-1]) );
}
}
else if(p[r] == '*' ){
for(int k = 0 ; k <= i ; k++){
l2 = k-1;
rel(i,j,k,j-1,l-l2-(cnt[l]-cnt[l2]));
}
}
}
}
初始化开头结尾是*的时候
dp[0][0] = make_pair(true,0);
if(p[n-1] == '*') dp[1][0] = make_pair(true,0);
if(p[0] == '*') dp[0][1] = make_pair(true,0);
tip2:
当然了。。这样并不能ac,因为这样字典序并不是最小的,只能保证长度是最小的。因为如果倒着去输出答案就会优先让中间那一段的字典序最优。所以换了一种dp,dp[I][j]表示从j到j+i-1匹配上的字符串是什么样的,也就是说i是长度,这样写起来更方便一些.类似区间dp,比当前长度小的,从任意字符开始匹配上的最小字符串都求好了,
下面用k表示I+j-1 当前区间是【j,k】
如果s[j] s[k]都是星,可以两个都放空字符串
dp[I][j] = dp[I-2][j+1]
右边的星为空:
dp[I][j] = dp[I-1][j]
左边为空:
dp[I][j] = dp[I-1][j+1]
如果s[j]是星,s[k]不是。
左边这个星为空:
dp[I][j] = dp[I-1][j+1],
不为空:
dp[I][j] = s[k]+dp[I-1][j]+s[k];
(这个星可能之前对I-1长度时候已经填了东西)
s[k]为星同理。
都不是星判断一下s[j] s[k]是否可以一样(?的存在)
但是这个sgu对内存要求挺高的。。然后我们发现,每次的dp之和上两次的长度,也就是I-1 I-2有关系,那么可以三维滚动。。。p是i-2,q是i-1,now是现在。。
p = (p+1)%3;q = (q+1)%3;now = (now+1)%3;
松弛的时候,比较字符串长度,长度相同,比较字典序
void rel(const string s,int now, int i){
if((int)dp[now][i] .size() > (int)s.size() || ((int)dp[now][i] .size()==(int)s.size()&&dp[now][i] > s))
dp[now][i] = s;
}
全部代码:
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
using namespace std;
typedef pair<bool,string>pii;
const int maxn = 260;
const int maxm = maxn*3;
string none = "";
char a[maxn], s[maxm],tmp;
string dp[3][maxm];
int p ,q,now,n ,len;
void init(){
scanf("%s", a);
len = strlen(a);
for(int i = 0 ; i < 400 ; i++)none += "NO";
for(int i = 0; i < len; i++){
if(a[i] == '!') s[++n] = '?', s[++n] = '?', s[++n] = '?';
else s[++n] = a[i];
}
for(int i = 1; i <= n; i++){
if(s[i] == '*') { dp[1][i] = "";}
else{
dp[1][i] = (s[i]=='?'?'a':s[i]);
}
}
}
void rel(const string s,int now, int i){
if((int)dp[now][i] .size() > (int)s.size() || ((int)dp[now][i] .size()==(int)s.size()&&dp[now][i] > s))
dp[now][i] = s;
}
void sov(){
p = 0,q = 1, now = 2;
for(int i = 2; i <= n; i++){
for(int j = 1; j <= n; j++){
int k = i+j-1;
if(k > n) break;
if(s[j] == '*' && s[k] == '*'){
dp[now][j] = dp[p][j+1] ;//都是空
//cout << p <<" "<<j+1 <<" "<<dp[p][j+1] <<endl;
rel(dp[q][j] ,now, j);//右空
rel(dp[q][j+1] ,now, j);//左空
}
else if(s[j] == '*' && s[k] != '*'){
dp[now][j] = dp[q][j+1] ;
tmp = s[k];
if(s[k] == '?') tmp = 'a';
rel(tmp+dp[q][j] +tmp, now,j);
}
else if(s[k] == '*'){
dp[now][j] = dp[q][j] ;
tmp = s[j];
if(s[j] == '?') tmp = 'a';
//cout <<"j = "<<j <<" k " <<k<<dp[q][j] <<endl;
rel(tmp+dp[q][j+1] +tmp,now, j);
}
else{
if(s[j] == '?'){
tmp = s[k];
if(s[k] == '?') tmp = 'a';
dp[now][j] = tmp+dp[p][j+1] +tmp;
}
else if(s[k] == '?' || s[j] == s[k]) dp[now][j] = s[j]+dp[p][j+1] +s[j];
else dp[now][j] = none;
}
}
p = (p+1)%3;q = (q+1)%3;now = (now+1)%3;
}
}
bool check(const string s){
for(int i = 0, j = s.size()-1; i < j; i++, j--)
if(s[i] != s[j]) return false;
return true;
}
void print(){
now = (now+3-1)%3;
if(check(dp[now][1] ) ){
cout << "YES" << endl;
cout << dp[now][1] << endl;
}
else cout << "NO" << endl;
}
int main(){
init();
sov();
print();
return 0;
}