【同向双指针】2023Q1A-有效子字符串
【同向双指针】2023Q1A-有效子字符串
题目描述与示例
本题练习地址:https://www.algomooc.com/problem/P3115
题目
输入两个字符串S和L,都只包含小写字母,len(S) <= 100,len(L) <= 500000。判断S是否是L的有效子字符串。
- 判定规则:
S中的每个字符在L中都能找到(可以不连续),且S在L中字符的前后顺序与S中顺序要保持一致。
例如:
S = "ace"是L = "abcde"的一个子序列,且有效字符是a、c、e,而"aec"不是有效子序列,且有效字符只有a、e(因为相对位置不同)。
输入
输入两个字符串S和L,都只包含小写字母,len(S) <= 100,len(L) <= 500000,先输入S再输入L 每个字符串占一行。
输出描述
S`串**最后一个有效字符**在`L`中的位置,首位从`0`开始计算。无有效字符返回 `-1示例一
输入
ace
abcde输出
4示例二
输入
fgh
abcde输出
-1解题思路
注意,本题和LC392. 判断子序列几乎完全一致。唯一的不同之处在于本题不仅要判断
S能够由L构成,还要得到S串最后一个有效字符在L中的位置。这实际上进一步提醒我们应该用贪心思想来解决这个问题。
需要特别注意,经过同学考试测试,对于例子
he
ehh应该输出
1这是因为,S = he虽然在L = ehh不能够完全匹配,但是能够匹配第一个字符h,这个h是S的**最后一个有效字符,**在L中第一次出现的位置是1,故输出答案为1。
仅需一个简单地例子就可以看出为何要使用贪心思想:假设L = "abac",S = "abc"。当我们做字符匹配时,当然会选择L中的第一个"a"而不是第二个"a"来与S中的"a"进行匹配。因为只有选择了尽量靠前的"a",才能使得S更有可能由L中的字符按照顺序构成。
故我们只需设置两个同向双指针pl和ps,分别用于L和S从左到右的遍历。当
S[ps] == L[pl]时,说明当前L中字符能与S匹配,此时应该记录ans = pl,是当前遇到的S中的最新的一个有效字符在L中的索引,同时pl和ps各自前进。S[ps] != L[pl]时,说明当前L中字符不能与S匹配,此时ps不应该移动,pl前进一位,去寻找L中下一个能和S[ps]匹配的字符。
上述过程应该在一个while循环中进行,退出循环的条件是pl到达L末尾nl或者ps到达S末尾ns。故上述算法的主体代码为
while(ps < ns and pl < nl):
if S[ps] == L[pl]:
ans = pl
ps += 1
pl += 1
else:
pl += 1我们可以初始化ans为-1,那么只要在循环中没有进入if S[ps] == L[pl]的分支,就说明S中没有任意一个字符能够跟L进行匹配,最终结果应该为-1。最终输出ans即可。
代码
Python
# 题目:2024D-有效子字符串
# 分值:100
# 作者:许老师-闭着眼睛学数理化
# 算法:贪心+同向双指针
# 代码看不懂的地方,请直接在群上提问
S = input()
L = input()
ans = -1
ns, nl, ps, pl = len(S), len(L), 0, 0
while(ps < ns and pl < nl):
# 如果S[ps]等于L[pl],先记录pl为ans,且ps和pl都向前移动
if S[ps] == L[pl]:
ans = pl
ps += 1
pl += 1
# 如果S[ps]不等于L[pl],只有pl向前移动
else:
pl += 1
print(ans)Java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 输入S和L
String S = scanner.nextLine();
String L = scanner.nextLine();
int ans = -1;
int ns = S.length(), nl = L.length();
int ps = 0, pl = 0;
while (ps < ns && pl < nl) {
// 如果S[ps]等于L[pl],先记录pl为ans,且ps和pl都向前移动
if (S.charAt(ps) == L.charAt(pl)) {
ans = pl;
ps++;
pl++;
}
// 如果S[ps]不等于L[pl],只有pl向前移动
else {
pl++;
}
}
System.out.println(ans);
}
}C++
#include <iostream>
#include <string>
using namespace std;
int main() {
string S, L;
// 输入S和L
cin >> S >> L;
int ans = -1;
int ns = S.length(), nl = L.length();
int ps = 0, pl = 0;
while (ps < ns && pl < nl) {
// 如果S[ps]等于L[pl],先记录pl为ans,且ps和pl都向前移动
if (S[ps] == L[pl]) {
ans = pl;
ps++;
pl++;
}
// 如果S[ps]不等于L[pl],只有pl向前移动
else {
pl++;
}
}
cout << ans << endl;
return 0;
}时空复杂度
时间复杂度:O(N+M)。同向双指针算法,每一个字符仅需遍历一次。
空间复杂度:O(1)。仅需要用到若干常数变量。
N为S的长度,M为L的长度。
