0%

1625.执行操作后字典序最小的字符串

1625.执行操作后字典序最小的字符串

题目描述:

给出一个长度为 $ n $ 的字符串 $ s $ ,以及两个整数 $ a,b $ ,其中 $ n $ 为偶数。

两种操作:

  • 累加:加 $ a $ 加到 $ s $ 中下标为奇数的元素上。超过 $ 9 $ 时变成 $ 0 $ 。
  • 轮转:将 $ s $ 向右轮转 $ b $ 位。

对 $ s $ 可以执行上述任意次操作,返回能够得到的字典序最小的字符串。

数据范围:
$ 2\le n \le 100,1\le a \le 9, 1\le b \le n - 1 $

题解:

解法一:

直接bfs,每次生成两个分支,直到队列为空。

解法二:

枚举。因为两个操作可以独立分离,因此可以直接枚举轮换的次数和累加的次数。因为轮换最多轮换 $ n $ 次,累加最多累加 $ 10 $ 次。但是 $ b $ 分奇偶,如果为奇数,累加时需要枚举奇数位置和偶数位置;如果为偶数,只需要枚举偶数位置。因此复杂度为 $ O(n^2 \times 10^2) $ .

考虑优化。假设轮换 $ x $ 次,最终的起始位置为 $ z = xb \mod{n} $ ,即 $ z = xb - ny $ ,根据裴蜀定理,该方程有解则必有 $ z $ 被 $ \gcd(b,n) $ 整除。因此枚举轮转只需要枚举 $ [0, n) $ 中 $ gcd(b,n) $ 的倍数即可。

再优化。由于轮转和累加可以分开考虑,同时需要使得字典序最小。因此只需要考虑 $ t[1] $ 和 $ t[0] $ 即可。先找到使得 $ t[1] $ 最小的累加次数, $ b $ 为奇数的话,再找到使得 $ t[0] $ 最小的累加次数。

会有一个疑问?如果存在多个累加次数得到的 $ t[1] $ 相等,但是 $ t[3] $ 不一样,那么就会有问题。

这种情况应该是不存在的,如果存在两个累加次数 $ t[1] $ 相等,那么说明这个累加次数得到的累加值是 $ 10 $ 的倍数,那么 $ t[3] $ 也会是相等的。

代码:

优化前:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Solution
{
public:
const static int maxn = 1e5 + 10;
const static int maxm = 1e5 + 10;
const static int INF = 0x3f3f3f3f;
string findLexSmallestString(string s, int a, int b)
{
int n = s.size();
string ans = s;
s = s + s;
vector<bool> vis(s.size(), false);
// 枚举轮转后的起点,第一次遇到vis=true时,后面全是循环
for (int start = 0; vis[start] == false; start = (start + b) % n)
{
vis[start] = true;
for (int j = 0; j < 10; ++j)
{
int k_max = (b & 1) ? 9 : 0;
for (int k = 0; k <= k_max; ++k)
{
string tmp = s.substr(start, n);
for (int p = 1; p < n; p += 2)
{
tmp[p] = '0' + (tmp[p] - '0' + j * a) % 10;
}
for (int p = 0; p < n; p += 2)
{
tmp[p] = '0' + (tmp[p] - '0' + k * a) % 10;
}
ans = min(ans, tmp);
}
}
}
return ans;
}
};

优化后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
class Solution
{
public:
const static int maxn = 1e5 + 10;
const static int maxm = 1e5 + 10;
const static int INF = 0x3f3f3f3f;
string findLexSmallestString(string s, int a, int b)
{
int n = s.size();
string ans = s;
s = s + s;
int gcd = __gcd(b, n);
int minx, times, tmp;
string t;
// 枚举轮转后的起点,第一次遇到vis=true时,后面全是循环
for (int start = 0; start < n; start += gcd)
{
t = s.substr(start, n);
times = 0;
minx = 9;
for (int j = 0; j < 10; ++j)
{
tmp = (t[1] - '0' + j * a) % 10;
if (tmp < minx)
{
minx = tmp;
times = j;
}
}
for (int j = 1; j < n; j += 2)
{
t[j] = '0' + (t[j] - '0' + times * a) % 10;
}
if (b & 1)
{
times = 0;
minx = 9;
for (int j = 0; j < 10; ++j)
{
tmp = (t[0] - '0' + j * a) % 10;
if (tmp < minx)
{
minx = tmp;
times = j;
}
}
for (int j = 0; j < n; j += 2)
{
t[j] = '0' + (t[j] - '0' + times * a) % 10;
}
}
ans = min(ans, t);
}
return ans;
}
};

image-20230319183935590