题目描述
给你一个字符串 s
以及两个整数 a
和 b
。其中,字符串 s
的长度为偶数,且仅由数字 0
到 9
组成。
你可以在 s
上按任意顺序多次执行下面两个操作之一:
- 累加:将
a
加到 s
中所有下标为奇数的元素上(下标从 0 开始)。数字一旦超过 9
就会变成 0
,如此循环往复。例如,s = "3456"
且 a = 5
,则执行此操作后 s
变成 "3951"
。
- 轮转:将
s
向右轮转 b
位。例如,s = "3456"
且 b = 1
,则执行此操作后 s
变成 "6345"
。
请你返回在 s
上执行上述操作任意次后可以得到的 字典序最小 的字符串。
如果两个字符串长度相同,那么字符串 a
字典序比字符串 b
小可以这样定义:在 a
和 b
出现不同的第一个位置上,字符串 a
中的字符出现在字母表中的时间早于 b
中的对应字符。例如,"0158”
字典序比 "0190"
小,因为不同的第一个位置是在第三个字符,显然 '5'
出现在 '9'
之前。
示例 1:
输入:s = "5525", a = 9, b = 2
输出:"2050"
解释:执行操作如下:
初态:"5525"
轮转:"2555"
累加:"2454"
累加:"2353"
轮转:"5323"
累加:"5222"
累加:"5121"
轮转:"2151"
累加:"2050"
无法获得字典序小于 "2050" 的字符串。
示例 2:
输入:s = "74", a = 5, b = 1
输出:"24"
解释:执行操作如下:
初态:"74"
轮转:"47"
累加:"42"
轮转:"24"
无法获得字典序小于 "24" 的字符串。
示例 3:
输入:s = "0011", a = 4, b = 2
输出:"0011"
解释:无法获得字典序小于 "0011" 的字符串。
提示:
2 <= s.length <= 100
s.length
是偶数
s
仅由数字 0
到 9
组成
1 <= a <= 9
1 <= b <= s.length - 1
解法
方法一:BFS
本题数据规模较小,我们可以使用 BFS 暴力搜索所有可能的状态,然后取字典序最小的状态即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | class Solution:
def findLexSmallestString(self, s: str, a: int, b: int) -> str:
q = deque([s])
vis = {s}
ans = s
while q:
s = q.popleft()
if ans > s:
ans = s
t1 = ''.join(
[str((int(c) + a) % 10) if i & 1 else c for i, c in enumerate(s)]
)
t2 = s[-b:] + s[:-b]
for t in (t1, t2):
if t not in vis:
vis.add(t)
q.append(t)
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 | class Solution {
public String findLexSmallestString(String s, int a, int b) {
Deque<String> q = new ArrayDeque<>();
q.offer(s);
Set<String> vis = new HashSet<>();
vis.add(s);
String ans = s;
int n = s.length();
while (!q.isEmpty()) {
s = q.poll();
if (ans.compareTo(s) > 0) {
ans = s;
}
char[] cs = s.toCharArray();
for (int i = 1; i < n; i += 2) {
cs[i] = (char) (((cs[i] - '0' + a) % 10) + '0');
}
String t1 = String.valueOf(cs);
String t2 = s.substring(n - b) + s.substring(0, n - b);
for (String t : List.of(t1, t2)) {
if (vis.add(t)) {
q.offer(t);
}
}
}
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 | class Solution {
public:
string findLexSmallestString(string s, int a, int b) {
queue<string> q{{s}};
unordered_set<string> vis{{s}};
string ans = s;
int n = s.size();
while (!q.empty()) {
s = q.front();
q.pop();
ans = min(ans, s);
string t1 = s;
for (int i = 1; i < n; i += 2) {
t1[i] = (t1[i] - '0' + a) % 10 + '0';
}
string t2 = s.substr(n - b) + s.substr(0, n - b);
for (auto& t : {t1, t2}) {
if (!vis.count(t)) {
vis.insert(t);
q.emplace(t);
}
}
}
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 | func findLexSmallestString(s string, a int, b int) string {
q := []string{s}
vis := map[string]bool{s: true}
ans := s
n := len(s)
for len(q) > 0 {
s = q[0]
q = q[1:]
if ans > s {
ans = s
}
t1 := []byte(s)
for i := 1; i < n; i += 2 {
t1[i] = byte((int(t1[i]-'0')+a)%10 + '0')
}
t2 := s[n-b:] + s[:n-b]
for _, t := range []string{string(t1), t2} {
if !vis[t] {
vis[t] = true
q = append(q, t)
}
}
}
return ans
}
|
方法二:枚举
我们观察发现,对于累加操作,数字最多累加 $10$ 次,就会回到原来的状态;对于轮转操作,字符串最多轮转 $n$ 次,也会回到原来的状态。
因此,轮转操作最多产生 $n$ 种状态,如果轮转位数 $b$ 为偶数,累加操作只会对奇数位数字产生影响,因此总共产生 $n \times 10$ 种状态;如果轮转位数 $b$ 为奇数,累加操作既会对奇数位数字产生影响,也会对偶数位数字产生影响,因此总共产生 $n \times 10 \times 10$ 种状态。
所以,我们直接枚举所有的字符串状态,取字典序最小的状态即可。
时间复杂度 $O(n^2 \times 10^2)$,空间复杂度 $O(n)$。其中 $n$ 为字符串 $s$ 的长度。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | class Solution:
def findLexSmallestString(self, s: str, a: int, b: int) -> str:
ans = s
n = len(s)
s = list(s)
for _ in range(n):
s = s[-b:] + s[:-b]
for j in range(10):
for k in range(1, n, 2):
s[k] = str((int(s[k]) + a) % 10)
if b & 1:
for p in range(10):
for k in range(0, n, 2):
s[k] = str((int(s[k]) + a) % 10)
t = ''.join(s)
if ans > t:
ans = t
else:
t = ''.join(s)
if ans > t:
ans = t
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 | class Solution {
public String findLexSmallestString(String s, int a, int b) {
int n = s.length();
String ans = s;
for (int i = 0; i < n; ++i) {
s = s.substring(b) + s.substring(0, b);
char[] cs = s.toCharArray();
for (int j = 0; j < 10; ++j) {
for (int k = 1; k < n; k += 2) {
cs[k] = (char) (((cs[k] - '0' + a) % 10) + '0');
}
if ((b & 1) == 1) {
for (int p = 0; p < 10; ++p) {
for (int k = 0; k < n; k += 2) {
cs[k] = (char) (((cs[k] - '0' + a) % 10) + '0');
}
s = String.valueOf(cs);
if (ans.compareTo(s) > 0) {
ans = s;
}
}
} else {
s = String.valueOf(cs);
if (ans.compareTo(s) > 0) {
ans = s;
}
}
}
}
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 | class Solution {
public:
string findLexSmallestString(string s, int a, int b) {
int n = s.size();
string ans = s;
for (int i = 0; i < n; ++i) {
s = s.substr(n - b) + s.substr(0, n - b);
for (int j = 0; j < 10; ++j) {
for (int k = 1; k < n; k += 2) {
s[k] = (s[k] - '0' + a) % 10 + '0';
}
if (b & 1) {
for (int p = 0; p < 10; ++p) {
for (int k = 0; k < n; k += 2) {
s[k] = (s[k] - '0' + a) % 10 + '0';
}
ans = min(ans, s);
}
} else {
ans = min(ans, s);
}
}
}
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 | func findLexSmallestString(s string, a int, b int) string {
n := len(s)
ans := s
for _ = range s {
s = s[n-b:] + s[:n-b]
cs := []byte(s)
for j := 0; j < 10; j++ {
for k := 1; k < n; k += 2 {
cs[k] = byte((int(cs[k]-'0')+a)%10 + '0')
}
if b&1 == 1 {
for p := 0; p < 10; p++ {
for k := 0; k < n; k += 2 {
cs[k] = byte((int(cs[k]-'0')+a)%10 + '0')
}
s = string(cs)
if ans > s {
ans = s
}
}
} else {
s = string(cs)
if ans > s {
ans = s
}
}
}
}
return ans
}
|