给你两个字符串 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 <= 105
-
1 <= word2.length <= 104
-
word1
和word2
都只包含小写英文字母。
思路:
可以将一个字符串重新排列后使字符串 word 2 是重排字符串的前缀
等价于原始字符串覆盖字符串 word2 的所有字符
因此问题等价于计算字符串 word1 的覆盖字符串 word2 的所有字符的子字符串数目
题解:
class Solution { public long validSubstringCount(String word1, String word2) { // 当word1小于word2时,直接返回0 if (word1.length() < word2.length()){ return 0L; } // 滑动窗口 // 初始化结果 long result = 0; // 存储字符串word2中每个字符出现的次数 Map<Character, Integer> word2Map = new HashMap<>(); for (int i = 0; i < word2.length(); i++) { word2Map.put(word2.charAt(i), word2Map.getOrDefault(word2.charAt(i), 0) + 1); } // 记录滑动窗口中每个字符出现的次数 Map<Character, Integer> windowMap = new HashMap<>(); // 记录匹配字符的数量 Integer matchCount = 0; // 左指针 Integer left = 0; // 右指针 Integer right = 0; // 滑动右指针 while (right < word1.length()){ char rightChar = word1.charAt(right); windowMap.put(rightChar, windowMap.getOrDefault(rightChar, 0) + 1); // 判断字符出现次数是否匹配 if (word2Map.containsKey(rightChar) && windowMap.get(rightChar).equals(word2Map.get(rightChar))){ // 如果该字符在滑动窗口和word2中都出现过,且出现次数相同,则匹配数量加1 matchCount++; } // 当每个字符出现次数完全一致,则滑动左指针 while (matchCount == word2Map.size()){ // 从right开始,后续字符串全部符合要求 result += word1.length() - right; // 缩小左端,移除该字符 char leftChar = word1.charAt(left); if (windowMap.get(leftChar) > 0){ windowMap.put(leftChar, windowMap.get(leftChar) - 1); } // 判断频次是否匹配,如果不匹配则匹配数量减1 if (word2Map.containsKey(leftChar) && windowMap.get(leftChar) < word2Map.get(leftChar)){ matchCount--; } // 滑动左指针 left++; } // 滑动右指针 right++; } return result; }}