sgu 237Galaxy X: Episode I - Masters of Mind (dp)

题意:

给你一个包含’‘、’?’、’!’、’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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值