Medium
题目描述
给你两个字符串 word1 和 word2。
如果字符串 x 可以重新排列使得 word2 成为其前缀,那么字符串 x 被称为 有效 的。
返回 word1 中有效子字符串的总数。
示例 1:
输入:word1 = "bcca", word2 = "abc"
输出:1
解释:唯一的有效子字符串是 "bcca",可以重新排列为 "abcc",其中 "abc" 是前缀。
示例 2:
输入:word1 = "abcabc", word2 = "abc"
输出:10
解释:除了长度为 1 和 2 的子字符串外,所有子字符串都是有效的。
示例 3:
输入:word1 = "abcabc", word2 = "aaabc"
输出:0
约束条件:
1 <= word1.length <= 10^51 <= word2.length <= 10^4word1和word2只包含小写英文字母。
解题思路
解题思路
这道题要求找出 word1 中有多少个子字符串可以重新排列后使得 word2 成为其前缀。
核心观察:如果一个字符串 x 可以重新排列使得 word2 成为其前缀,那么 x 中每个字符的频次必须大于等于 word2 中对应字符的频次。
滑动窗口解法:
- 首先统计
word2中每个字符的频次作为需求 - 使用滑动窗口遍历
word1,维护当前窗口内字符的频次 - 对于每个右端点
right,找到最小的左端点left使得窗口[left, right]包含足够的字符 - 一旦找到这样的窗口,所有以
right为右端点且左端点<= left的子字符串都是有效的
优化细节:
- 使用变量
need记录还需要满足的字符种类数 - 当某个字符的频次刚好满足要求时,
need-- - 当
need == 0时,当前窗口有效
时间复杂度为 O(n),其中 n 是 word1 的长度,因为左右指针都最多移动 n 次。
代码实现
class Solution {
public:
long long validSubstringCount(string word1, string word2) {
vector<int> need(26, 0);
int required = 0;
// 统计 word2 中各字符的需求量
for (char c : word2) {
if (need[c - 'a'] == 0) required++;
need[c - 'a']++;
}
vector<int> window(26, 0);
int left = 0, formed = 0;
long long result = 0;
for (int right = 0; right < word1.length(); right++) {
char c = word1[right];
window[c - 'a']++;
// 如果当前字符的频次刚好满足需求
if (need[c - 'a'] > 0 && window[c - 'a'] == need[c - 'a']) {
formed++;
}
// 当窗口满足条件时,尝试缩小窗口
while (left <= right && formed == required) {
result += left + 1; // 以 right 为右端点的所有有效子字符串
char leftChar = word1[left];
window[leftChar - 'a']--;
if (need[leftChar - 'a'] > 0 && window[leftChar - 'a'] < need[leftChar - 'a']) {
formed--;
}
left++;
}
}
return result;
}
};
class Solution:
def validSubstringCount(self, word1: str, word2: str) -> int:
from collections import Counter
need = Counter(word2)
required = len(need)
window = {}
left = 0
formed = 0
result = 0
for right in range(len(word1)):
char = word1[right]
window[char] = window.get(char, 0) + 1
if char in need and window[char] == need[char]:
formed += 1
while left <= right and formed == required:
result += left + 1
left_char = word1[left]
window[left_char] -= 1
if left_char in need and window[left_char] < need[left_char]:
formed -= 1
left += 1
return result
public class Solution {
public long ValidSubstringCount(string word1, string word2) {
int[] need = new int[26];
int required = 0;
foreach (char c in word2) {
if (need[c - 'a'] == 0) required++;
need[c - 'a']++;
}
int[] window = new int[26];
int left = 0, formed = 0;
long result = 0;
for (int right = 0; right < word1.Length; right++) {
char c = word1[right];
window[c - 'a']++;
if (need[c - 'a'] > 0 && window[c - 'a'] == need[c - 'a']) {
formed++;
}
while (left <= right && formed == required) {
result += left + 1;
char leftChar = word1[left];
window[leftChar - 'a']--;
if (need[leftChar - 'a'] > 0 && window[leftChar - 'a'] < need[leftChar - 'a']) {
formed--;
}
left++;
}
}
return result;
}
}
var validSubstringCount = function(word1, word2) {
const need = new Array(26).fill(0);
let required = 0;
for (const c of word2) {
const idx = c.charCodeAt(0) - 97;
if (need[idx]
复杂度分析
| 复杂度类型 | 值 | 说明 |
|---|---|---|
| 时间复杂度 | O(n + m) | n 为 word1 长度,m 为 word2 长度,每个字符最多被访问两次 |
| 空间复杂度 | O(1) | 只使用固定大小的数组存储字符频次(26个字母) |
相关题目
- . Minimum Window Substring (Hard)