题目链接
题目描述
给定一个由小写英文字母和 '?' 组成的字符串 ,以及一个只由小写英文字母组成的字符串
。
任务是将 中的每一个 '?' 替换成一个小写英文字母,使得
是替换后
的一个子序列。
如果存在这样的构造方式,输出 "YES" 和任意一个构造后的 ;如果不存在,则输出 "NO"。
解题思路
这是一个子序列匹配问题,可以采用贪心算法来解决。一个非常有效的策略是从右向左进行匹配。
核心思想:为了最大化匹配成功的可能性,我们应该为 中的每个字符在
中寻找尽可能靠右的位置。例如,对于
的最后一个字符,如果我们在
中选择最右边的可能位置来匹配它,那么
的其余前缀部分将拥有最长的
前缀来进行匹配。
算法步骤:
- 我们使用两个指针,
指向字符串
的末尾,
指向字符串
的末尾。
- 我们将字符串
转换为一个字符数组,以便进行修改。
- 我们从后向前遍历
的字符数组(即
从
递减到
)。
- 在当前位置
:
- 检查是否仍然需要匹配
中的字符(即
),以及当前字符
是否能与
匹配。匹配的条件是
或者
。
- 如果可以匹配,我们就确定这个匹配。我们将
字符数组在
位置的字符更新为
,并将指针
向左移动一位,表示
的这个字符已经成功匹配。
- 如果不能匹配,或者
已经小于 0(意味着
已全部匹配完),我们就处理
的填充。如果
是一个 '?',为了构造一个确定的答案,我们统一将其替换为 'a'。
- 检查是否仍然需要匹配
- 当
遍历完整个
后,我们检查
的最终位置。
- 如果
,说明
的所有字符都找到了匹配位置,构造成功。输出 "YES" 和修改后的字符串。
- 如果
,说明
遍历结束后仍有
的字符未能匹配,构造失败。输出 "NO"。
- 如果
这个算法通过一次从右到左的遍历,同时完成了可行性判断和结果构造,时间复杂度是线性的。
代码
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
void solve() {
string s1, s2;
cin >> s1 >> s2;
int n = s1.length();
int m = s2.length();
int p1 = n - 1;
int p2 = m - 1;
while (p1 >= 0) {
if (p2 >= 0 && (s1[p1] == s2[p2] || s1[p1] == '?')) {
s1[p1] = s2[p2];
p2--;
} else if (s1[p1] == '?') {
s1[p1] = 'a';
}
p1--;
}
if (p2 < 0) {
cout << "YES" << endl;
cout << s1 << endl;
} else {
cout << "NO" << endl;
}
}
int main() {
// 多组测试数据,总输入量较大,建议使用关流语句
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
while (t-- > 0) {
solve(sc);
}
}
private static void solve(Scanner sc) {
String s1_str = sc.next();
String s2_str = sc.next();
char[] s1 = s1_str.toCharArray();
char[] s2 = s2_str.toCharArray();
int n = s1.length;
int m = s2.length;
int p1 = n - 1;
int p2 = m - 1;
while (p1 >= 0) {
if (p2 >= 0 && (s1[p1] == s2[p2] || s1[p1] == '?')) {
s1[p1] = s2[p2];
p2--;
} else if (s1[p1] == '?') {
s1[p1] = 'a';
}
p1--;
}
if (p2 < 0) {
System.out.println("YES");
System.out.println(new String(s1));
} else {
System.out.println("NO");
}
}
}
import sys
def solve():
s1 = list(sys.stdin.readline().strip())
s2 = list(sys.stdin.readline().strip())
n = len(s1)
m = len(s2)
p1 = n - 1
p2 = m - 1
while p1 >= 0:
if p2 >= 0 and (s1[p1] == s2[p2] or s1[p1] == '?'):
s1[p1] = s2[p2]
p2 -= 1
elif s1[p1] == '?':
s1[p1] = 'a'
p1 -= 1
if p2 < 0:
print("YES")
print("".join(s1))
else:
print("NO")
def main():
t = int(sys.stdin.readline())
for _ in range(t):
solve()
if __name__ == "__main__":
main()
算法及复杂度
- 算法:贪心算法 + 双指针。
- 时间复杂度:
。对于每个测试用例,我们都对字符串
进行一次线性扫描,总时间复杂度是所有测试用例中
长度的总和。
- 空间复杂度:
。主要是存储所有输入的字符串。在单个测试用例中,需要
的空间来存储字符数组。