1455F String and Operations(思维+贪心)

1455F String and Operations(思维+贪心)

Educational Codeforces Round 99 (Rated for Div. 2)

F. String and Operations

题面String and Operations

题意:给一个长度为 n n n 的字符串 s s s,其中只包含前 k k k 个小写字母。现在需要对字符串进行 n n n 次操作,第 i i i 次操作的操作对象是初始位于位置 i i i 的对应字符,共有 5 5 5 种操作:

  • 与上一个字符(如果存在)进行交换
  • 与下一个字符(如果存在)进行交换
  • 将该字符循环移动到下一个字符, a → b , b → c , . . . , ( a + k − 1 ) → a a \rightarrow b, b \rightarrow c, ...,(a+k-1) \rightarrow a ab,bc,...,(a+k1)a
  • 将该字符循环移动到上一个字符, c → b , b → a , a → ( a + k − 1 ) c \rightarrow b, b \rightarrow a,a \rightarrow (a+k-1) cb,ba,a(a+k1)
  • 不操作

求经过 n n n 次操作后能得到的最小字典序的字符串。

范围 1 ≤ n ≤ 500 , 2 ≤ k ≤ 26 1 \le n \le 500, 2 \le k \le 26 1n500,2k26

分析: 要让字典序最小,必须保证前面的字符尽可能小,经过简单分析可以发现,当前位置字符的最优解可以从以下几种情况中构造而来:

设前一个位置的字符为 p r e pre pre,当前位置字符为 x x x,下一个位置的字符为 n x t nxt nxt,下下个位置的字符为 n n x t nnxt nnxt

b e s t ( x ) + a n s [ i + 1 , n ] best(x)+ans[i+1, n] best(x)+ans[i+1,n]

b e x t ( n x t ) + x + a n s [ i + 2 , n ] bext(nxt)+x+ans[i+2, n] bext(nxt)+x+ans[i+2,n]

n x t + b e s t ( x ) + a n s [ i + 2 , n ] nxt+best(x)+ans[i+2, n] nxt+best(x)+ans[i+2,n]

n n x t + b e s t ( x ) + n x t + a n s [ i + 3 , n ] nnxt+best(x)+nxt+ans[i+3, n] nnxt+best(x)+nxt+ans[i+3,n]

其中 b e s t ( x ) best(x) best(x) 表示字符 x x x x x x 相邻字符中的最小字典序字符。

通过以上 4 4 4 种情况取最优解则可以保证当前 i i i 位置的字典序尽可能小,但是引出了另外一个问题,在处理第 i i i 个字符时有可能前一个位置的字符字典序并不是最小,前一个位置的最优解可以从以下几种情况中构造而来:

p r e + a n s [ i , n ] pre+ans[i, n] pre+ans[i,n]

x + p r e + a n s [ i + 1 , n ] x+pre+ans[i+1, n] x+pre+ans[i+1,n]

n x t + p r e + x + a n s [ i + 2 , n ] nxt+pre+x+ans[i+2, n] nxt+pre+x+ans[i+2,n]

注意在处理到 x x x 的时候,之前的所有字符一定无法继续操作了,因此不需要 b e s t best best,并且这里几种情况只涉及要位置的移动,对 x x x n x t nxt nxt 也不需要进行 b e s t best best

通过以上的逻辑,先判断前一个位置的字符是否可以优化,能优化就优化,不能优化就尝试优化当前位置的字符。

时间复杂度 O ( n ) O(n) O(n)

Code

#include <bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
 
inline int read()
{
    int s = 0, w = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        s = s * 10 + ch - '0', ch = getchar();
    return s * w;
}
 
const int MAXN = 500 + 10;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const double eps = 1e-9;
const double PI = acos(-1.0);
 
int n, m, k;
 
string str, ans;
 
char D(char c)
{
	return 'a' + ((c - 'a') - 1 + k) % k;
}
 
char U(char c)
{
	return 'a' + ((c - 'a') + 1) % k;
}
 
char best(char c)
{
	return min(c, min(D(c), U(c)));
}
 
signed main()
{
    int T = read();
    while (T--)
    {
        n = read(), k = read();
        cin >> str;
        ans = "";
        int idx = 0;
        while (idx < n)
        {
        	ans += str[idx];
        	if (idx - 1 >= 0)
        	{
        		char pre = ans[idx - 1];
        		int res = 0;
        		if (ans[idx] < ans[idx - 1])
        		{
        			ans[idx - 1] = ans[idx];
        			res = 1;
        		}
        		if (idx + 1 < n && str[idx + 1] < ans[idx - 1])
        		{
        			ans[idx - 1] = str[idx + 1];
        			ans += str[idx];
        			res = 2;
        		}
        		if (res) ans[idx] = pre;
        		if (res == 1)
        		{
        			idx++;
        			continue;
        		}
        		else if(res == 2)
        		{
        			idx += 2;
        			continue;
        		}
        	}
        	ans[idx] = best(ans[idx]);
        	int res = 0;
        	if (idx + 1 < n && best(str[idx + 1]) < ans[idx])
        	{
        		if (best(str[idx + 1]) == str[idx + 1])
        		{
        			ans += str[idx + 1];
        			swap(ans[idx], ans[idx + 1]);
        		}
        		else
        		{
        			ans[idx] = best(str[idx + 1]);
        			ans += str[idx];
        		}
        		res = 1;
        	}
        	if (idx + 2 < n && str[idx + 2] < ans[idx])
        	{
        		while (ans.length() < idx + 3) ans += 'a';
        		ans[idx] = str[idx + 2];
        		ans[idx + 1] = best(str[idx]);
        		ans[idx + 2] = str[idx + 1];
        		res = 2;
        	}
        	if (res == 0) idx++;
        	else if (res == 1) idx += 2;
        	else idx += 3;
        }
        cout << ans << endl;
    }
    return 0;
}

【END】感谢观看

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值