为了更好的阅读体检,可以查看我的算法学习博客
在线评测链接:P1046
题目内容
塔子哥是一名计算机科学家,他正在研究一种特殊的字符串。这个字符串由小写字母 ‘x’
和特殊字符 ‘*’
构成。现在,他想要将这个字符串中的每个字符 ‘*’
替换成
0
0
0 到
m
m
m 个小写字母 ‘y’
,并将替换后的字符串称为 "xy字符串"
。对于原始字符串
s
s
s ,可以用不同替换方式可以得到多种不相同的 "xy字符串"
,塔子哥现在想知道第
n
n
n 小的 "xy字符串"
,但是他现在忙于研究 "xy字符串”
的其他性质,你帮他解决这个问题吗?
假定有两个 "xy字符串"
分别为
s
1
s1
s1 、
s
2
s2
s2 ,这两个 "xy字符串"
满足以下特性:
- 如果 s 1 s1 s1 和 s 2 s2 s2 的长度不同,或者下标对应的字母不同,则认为 s 1 s1 s1 和 s 2 s2 s2 不相同
- 如果
s
1
s1
s1 要小于
s
2
s2
s2 的话,需要满足任意条件之一
- s 1 s1 s1 是 s 2 s2 s2 的前缀且 s 1 s1 s1 与 s 2 s2 s2 不相同
- 比较 s 1 s1 s1 和 s 2 s2 s2 ,从左往右第个字母不同, 且 s 1 s1 s1 的字母小于 s 2 s2 s2 的字母
输入描述
第一行输入一个数字 T T T ( 1 ≤ T ≤ 2500 1\le T \le 2500 1≤T≤2500 ) ,表示有 T T T 组测试用例。对于每一组用例:
第一行分别有 3 3 3 个数字, l l l ( 1 ≤ 1 ≤ 2500 1\le 1 \le 2500 1≤1≤2500 ) , m m m ( 0 ≤ m ≤ 2500 0\le m \le 2500 0≤m≤2500 ), n n n ( 1 ≤ n ≤ 1 0 18 1\le n \le 10^{18} 1≤n≤1018 )。
第二行有一个字符串
s
s
s 由
l
l
l 个字符构成,字符串由小写字母 'x'
和特殊字符 ‘*’
构成。
输出描述
对于每组测试用例,输出一行字符串,表示第
n
n
n 小的 "xy字符串"
样例
输入
2
4 1 3
x**x
2 4 3
x*
输出
xyyx
xyy
备注
T
T
T 组测试用例的字符串
l
l
l 加起来不会超过
2500
2500
2500 。对于每组测试用例,可以保证
n
n
n 不会超过 不相同的 "xy字符串"
的数量
思路:数学进制转换
将所有连续的 '*'
分为一组,
对于字典序来说,因为 'y'
的字典序大于 'x'
,所以 'y'
越靠后越好。
对于长度为
c
i
c_i
ci 的连续的 '*'
,共有
c
i
×
m
+
1
c_i\times m + 1
ci×m+1 个选择,即
0
0
0 个 'y'
,
1
1
1 个 'y'
,
2
2
2 个 'y'
,…,
c
i
×
m
c_i\times m
ci×m 个 'y'
。
本题就是个进制转换的问题。
假设共有
l
e
n
len
len 个组,每个组有
c
i
(
c
i
≥
1
)
c_i(c_i\geq 1)
ci(ci≥1) 个 '*'
。
对于第
i
i
i 组,其权值为
(
c
i
+
1
×
m
+
1
)
×
(
c
i
+
2
×
m
+
1
)
×
⋯
×
(
c
n
×
m
+
1
)
(c_{i+1}\times m+1)\times (c_{i+2}\times m+1)\times \cdots \times (c_{n}\times m+1)
(ci+1×m+1)×(ci+2×m+1)×⋯×(cn×m+1)
举个例子,对于十进制数 123
来说:
- 3 3 3 是个位,权值为 1 1 1
- 2 2 2 是十位,权值为 10 10 10,其后面的个位有 10 10 10 种选择,即 [ 0 , 9 ] [0,9] [0,9]
- 1 1 1 是百位,权值为 100 100 100,其后面的十位和个位共有 100 100 100 种选择,即 [ 0 , 99 ] [0,99] [0,99]
对于本题第 i i i 组来说,其权值为 ∏ j = i + 1 n ( c j × m + 1 ) \prod\limits_{j=i+1}^n (c_j\times m+1) j=i+1∏n(cj×m+1)
类似题目推荐
LeetCode
代码
CPP
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
void solve() {
int len, m;
ll n;
cin >> len >> m >> n;
string s;
cin >> s;
// 将所有连续的 * 分为一组
vector<PII> vec;
for (int i = 0; i < len; ++i) {
if (s[i] != '*') continue;
int j = i + 1;
while (j < len && s[j] == '*') j += 1;
vec.emplace_back(i, j - 1);
i = j - 1;
}
// 如果原串中没有*,或者求第一个,或者*不能转换为至少一个y,则所有的*全部去除即可
if (vec.empty() || n == 1 || m == 0) {
string ans;
for (auto c: s) if (c != '*') ans += c;
cout << ans << "\n";
return;
}
// 进制转换中,最小值为 0,所有每个数都要减 1,即求 n-1
// 比如 0 是进制中最小的数
n -= 1;
vector<int> cnt(vec.size());
for (int i = int(vec.size()) - 1; i >= 0; --i) {
// 计算 ci
int ci = m * (vec[i].second - vec[i].first + 1) + 1;
// 可以类比为十进制转二进制的解法
cnt[i] = n % ci;
// 每次除去 mod ,等于得到了 i-1 的权值
n /= ci;
}
// 将第 i 组的 * 转换为 cnt[i] 个 y
string ans;
for (int i = 0, idx = 0; i < len; ++i) {
if (s[i] != '*') {
ans += s[i];
continue;
}
int j = i + 1;
while (j < len && s[j] == '*') j += 1;
if (cnt[idx] > 0) {
ans += string(cnt[idx], 'y');
}
idx += 1;
i = j - 1;
}
cout << ans << "\n";
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1;
cin >> T;
while (T--) solve();
return 0;
}
python
T = int(input())
for _ in range(T):
length, m, n = map(int, input().split())
s = input()
# 将所有连续的 * 分为一组
vec = []
i = 0
while i < length:
if s[i] != '*':
i += 1
continue
j = i + 1
while j < length and s[j] == '*':
j += 1
vec.append((i, j - 1))
i = j
# 如果原串中没有*,或者求第一个,或者*不能转换为至少一个y,则所有的*全部去除即可
if not vec or n == 1 or m == 0:
ans = ''.join(c for c in s if c != '*')
print(ans)
continue
# 进制转换中,最小值为 0,所有每个数都要减 1,即求 n-1
# 比如 0 是进制中最小的数
n -= 1
cnt = [0] * len(vec)
for i in range(len(vec) - 1, -1, -1):
# 计算 ci
ci = m * (vec[i][1] - vec[i][0] + 1) + 1
# 可以类比为十进制转二进制的解法
cnt[i] = n % ci
# 每次除去 mod ,等于得到了 i-1 的权值
n //= ci
# 将第 i 组的 * 转换为 cnt[i] 个 y
ans = ''
idx = 0
i = 0
while i < length:
if s[i] != '*':
ans += s[i]
i += 1
continue
j = i + 1
while j < length and s[j] == '*':
j += 1
if cnt[idx] > 0:
ans += 'y' * cnt[idx]
idx += 1
i = j
print(ans)
Java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int T = in.nextInt();
while (T-- > 0) {
int len = in.nextInt();
int m = in.nextInt();
long n = in.nextLong();
String s = in.next();
// 将所有连续的 * 分为一组
int[][] vec = new int[len][2];
int cnt = 0;
for (int i = 0; i < len; ++i) {
if (s.charAt(i) != '*') continue;
int j = i + 1;
while (j < len && s.charAt(j) == '*') j += 1;
vec[cnt][0] = i;
vec[cnt][1] = j - 1;
cnt++;
i = j - 1;
}
// 如果原串中没有*,或者求第一个,或者*不能转换为至少一个y,则所有的*全部去除即可
if (cnt == 0 || n == 1 || m == 0) {
StringBuilder ans = new StringBuilder();
for (int i = 0; i < len; ++i) {
char c = s.charAt(i);
if (c != '*') ans.append(c);
}
System.out.println(ans);
continue;
}
// 进制转换中,最小值为 0,所有每个数都要减 1,即求 n-1
// 比如 0 是进制中最小的数
n -= 1;
int[] c = new int[cnt];
for (int i = cnt - 1; i >= 0; --i) {
// 计算 ci
int ci = m * (vec[i][1] - vec[i][0] + 1) + 1;
// 可以类比为十进制转二进制的解法
c[i] = (int) (n % ci);
// 每次除去 mod ,等于得到了 i-1 的权值
n /= ci;
}
// 将第 i 组的 * 转换为 cnt[i] 个 y
StringBuilder ans = new StringBuilder();
for (int i = 0, idx = 0; i < len; ++i) {
if (s.charAt(i) != '*') {
ans.append(s.charAt(i));
continue;
}
int j = i + 1;
while (j < len && s.charAt(j) == '*') j += 1;
if (c[idx] > 0) {
ans.append("y".repeat(c[idx]));
}
idx += 1;
i = j - 1;
}
System.out.println(ans);
}
}
}
Go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
in := bufio.NewReader(os.Stdin)
var T int
fmt.Fscanln(in, &T)
for T > 0 {
T -= 1
var l, m int
var n int64
fmt.Fscan(in, &l, &m, &n)
n -= 1
var s string
fmt.Fscan(in, &s)
tmp := []byte(s)
ans := make([]byte, 0)
for i := l - 1; i >= 0; i-- {
if tmp[i] == 'x' {
ans = append(ans, 'x')
continue
}
var base int64
for i >= 0 && tmp[i] == '*' {
base += int64(m)
i--
}
base += 1
i += 1
if n == 0 || base == 0 {
continue
}
for j := int64(0); j < (n % base); j++ {
ans = append(ans, 'y')
}
n /= base
}
for i := 0; i < len(ans)/2; i++ {
ans[i], ans[len(ans)-i-1] = ans[len(ans)-i-1], ans[i]
}
fmt.Println(string(ans))
}
}
题目内容均收集自互联网,如如若此项内容侵犯了原著者的合法权益,可联系我: (CSDN网站注册用户名: 塔子哥学算法) 进行删除。