【同向双指针】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
的长度。