多多的灰度发布
拼多多技术岗 7月19号笔试 第一题
题目内容
多多在维护一批编号 1 , 2 , ... , n 1,\ 2,\ \dots,\ n 1, 2, ..., n 排列的实例。每个实例只有两种状态: 0 0 0 表示使用旧版本, 1 1 1 表示使用新版本。
多多在灰度发布平台执行了一次操作:
- 选择一个非空连续区间 L , R L,\\ R L, R
- 选择一个目标状态 v v v,其中 v v v 为 0 0 0 或 1 1 1
- 把区间内所有实例的状态都设为 v v v
区间中可以包含原本就已经处于状态 v v v 的实例,但这次操作必须至少改变一个实例的状态。发布前后的实例状态串 A A A 和 B B B 被完整保留,但操作日志丢失了,保证至少存在一种操作,可以把 A A A 变为 B B B。
请判断这次操作能否被唯一确定。若合法的三元组 ( L , R , V ) (L,\ R,\ V) (L, R, V) 恰好只有一个,输出它,否则输出 − 1 -1 −1。 注意:只要 L 、 R L、R L、R 或 V V V 中有任意一项不同,就视为不同的操作,即使它们得到的发布后状态完全相同。
输入描述
第一行包含一个正整数 T T T,表示测试用例的数量。
对于每个测试用例:
第一行包含一个整数 n n n,表示实例数量。
第二行包含一个长度为 n n n 的 01 01 01 串 A A A,表示发布前的状态。
第三行包含一个长度为 n n n 的 01 01 01 串 B B B,表示发布后的状态。
输出描述
对于每个测试用例:
如果操作唯一,输出一行三个整数 L R V L\ R\ V L R V。
如果不存在唯一操作,输出一行 − 1 -1 −1。
补充说明:
- 1 ≤ T ≤ 10 1 \le T \le 10 1≤T≤10
- A A A 和 B B B 均为长度恰好为 n n n 的 01 01 01 串;
- 单个输入文件中,所有测试用例的 n n n 之和不超过 2 ∗ 10 5 2 * 10^5 2∗105;
- 保证每组数据至少存在一种合法操作。
样例1
输入
6
5
00000
01110
5
01000
01110
6
111111
100001
7
0001000
0111110
1
0
1
5
00010
01110
输出
2 4 1
-1
2 5 0
2 6 1
1 1 1
-1
题解和思路
思路
实现思路:逻辑分析
- 对于每组输入,通过遍历发布前/后字符串找到对应
start 第一个不同位置和end 最后一个不同的位置 - 根据第一步得出start和end可以完成一下判断
- start = -1,说明发布前/后字符串完全相同,不存在唯一解。
- 将发布前变更为发布后,唯一可能的v就是`Bstart, 判断start, end是否全为v,不全为v说明根本无法通过一次操作将发布前变更为发布后。
- 由于区间中可以包含原本就已经处于状态 v v v 的实例,通过上面判断之后,可以得知start, end, v肯定是一个合法操作。要保证是否唯一,主要看是否能够进行区间左右扩展。
B[start - 1] == v说明能往左侧扩展,答案不唯一B[end-+1] == v说明能往右侧扩展,答案不唯一
- 算法平均时间复杂度为
O(n)
C++
cpp
#include<bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
string A,B;
cin >> A;
cin >> B;
// 不同的开始和结束位置
int start = -1;
int end = -1;
for (int i = 0; i < n; i++) {
if (A[i] != B[i]) {
if (start == -1) {
start = i;
}
end = i;
}
}
// 没有变化,不存在合法操作
if (start == -1) {
cout << -1 << endl;
continue;
}
// 确定v
int v = B[start] - '0';
bool ok = true;
// 判断B [start,end]是否都为v v肯定为B[start]
for (int i = start; i <= end; i++) {
if (B[i] != B[start]) {
ok = false;
break;
}
}
// 左右扩展,判断是否唯一
if (ok && start > 0 && B[start - 1] == B[start]) {
ok = false;
}
if (ok && end < n - 1 && B[end + 1] == B[start]) {
ok = false;
}
// 不唯一
if (!ok) {
cout << -1 << endl;
} else {
cout << start + 1 << " " << end + 1 << " " << B[start] - '0' << endl;
}
}
return 0;
}
Java
java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int T = sc.nextInt();
while (T-- > 0) {
int n = sc.nextInt();
String A = sc.next();
String B = sc.next();
// 不同的开始和结束位置
int start = -1;
int end = -1;
for (int i = 0; i < n; i++) {
if (A.charAt(i) != B.charAt(i)) {
if (start == -1) {
start = i;
}
end = i;
}
}
// 没有变化,不存在合法操作
if (start == -1) {
System.out.println(-1);
continue;
}
// 确定v
int v = B.charAt(start) - '0';
boolean ok = true;
// 判断B[start,end]是否都为v,v肯定为B[start]
for (int i = start; i <= end; i++) {
if (B.charAt(i) != B.charAt(start)) {
ok = false;
break;
}
}
// 左右扩展,判断是否唯一
if (ok && start > 0 && B.charAt(start - 1) == B.charAt(start)) {
ok = false;
}
if (ok && end < n - 1 && B.charAt(end + 1) == B.charAt(start)) {
ok = false;
}
// 不唯一
if (!ok) {
System.out.println(-1);
} else {
System.out.println((start + 1) + " " + (end + 1) + " " + v);
}
}
}
}
python
python
T = int(input())
for _ in range(T):
n = int(input())
A = input().strip()
B = input().strip()
# 不同的开始和结束位置
start = -1
end = -1
for i in range(n):
if A[i] != B[i]:
if start == -1:
start = i
end = i
# 没有变化,不存在合法操作
if start == -1:
print(-1)
continue
# 确定v
v = int(B[start])
ok = True
# 判断B[start,end]是否都为v,v肯定为B[start]
for i in range(start, end + 1):
if B[i] != B[start]:
ok = False
break
# 左右扩展,判断是否唯一
if ok and start > 0 and B[start - 1] == B[start]:
ok = False
if ok and end < n - 1 and B[end + 1] == B[start]:
ok = False
# 不唯一
if not ok:
print(-1)
else:
print(start + 1, end + 1, v)
Javascript
js
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const input = [];
rl.on("line", (line) => {
input.push(line);
});
rl.on("close", () => {
let idx = 0;
const T = Number(input[idx++]);
const ans = [];
for (let t = 0; t < T; t++) {
const n = Number(input[idx++]);
const A = input[idx++];
const B = input[idx++];
// 不同的开始和结束位置
let start = -1;
let end = -1;
for (let i = 0; i < n; i++) {
if (A[i] !== B[i]) {
if (start === -1) {
start = i;
}
end = i;
}
}
// 没有变化,不存在合法操作
if (start === -1) {
ans.push("-1");
continue;
}
// 确定v
const v = Number(B[start]);
let ok = true;
// 判断B[start,end]是否都为v,v肯定为B[start]
for (let i = start; i <= end; i++) {
if (B[i] !== B[start]) {
ok = false;
break;
}
}
// 左右扩展,判断是否唯一
if (ok && start > 0 && B[start - 1] === B[start]) {
ok = false;
}
if (ok && end < n - 1 && B[end + 1] === B[start]) {
ok = false;
}
// 不唯一
if (!ok) {
ans.push("-1");
} else {
ans.push(`${start + 1} ${end + 1} ${v}`);
}
}
console.log(ans.join("\n"));
});
Go
go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
in := bufio.NewReader(os.Stdin)
var T int
fmt.Fscan(in, &T)
for ; T > 0; T-- {
var n int
fmt.Fscan(in, &n)
var A, B string
fmt.Fscan(in, &A)
fmt.Fscan(in, &B)
// 不同的开始和结束位置
start := -1
end := -1
for i := 0; i < n; i++ {
if A[i] != B[i] {
if start == -1 {
start = i
}
end = i
}
}
// 没有变化,不存在合法操作
if start == -1 {
fmt.Println(-1)
continue
}
// 确定v
v := int(B[start] - '0')
ok := true
// 判断B[start,end]是否都为v,v肯定为B[start]
for i := start; i <= end; i++ {
if B[i] != B[start] {
ok = false
break
}
}
// 左右扩展,判断是否唯一
if ok && start > 0 && B[start-1] == B[start] {
ok = false
}
if ok && end < n-1 && B[end+1] == B[start] {
ok = false
}
// 不唯一
if !ok {
fmt.Println(-1)
} else {
fmt.Println(start+1, end+1, v)
}
}
}