给你一个字符串 word 和一个字符串数组 forbidden 。
如果一个字符串不包含 forbidden 中的任何字符串,我们称这个字符串是 合法 的。
请你返回字符串 word 的一个 最长合法子字符串 的长度。
子字符串 指的是一个字符串中一段连续的字符,它可以为空。
示例 1:
输入:word = "cbaaaabc", forbidden = ["aaa","cb"]
输出:4
解释:总共有 9 个合法子字符串:"c" ,"b" ,"a" ,"ba" ,"aa" ,"bc" ,"baa" ,"aab" 和 "aabc" 。最长合法子字符串的长度为 4 。
其他子字符串都要么包含 "aaa" ,要么包含 "cb" 。
示例 2:
输入:word = "leetcode", forbidden = ["de","le","e"]
输出:4
解释:总共有 11 个合法子字符串:"l" ,"t" ,"c" ,"o" ,"d" ,"tc" ,"co" ,"od" ,"tco" ,"cod" 和 "tcod" 。最长合法子字符串的长度为 4 。
所有其他子字符串都至少包含 "de" ,"le" 和 "e" 之一。
提示:
1 <= word.length <= 1e5
word 只包含小写英文字母。
1 <= forbidden.length <= 1e5
1 <= forbidden[i].length <= 10
forbidden[i] 只包含小写英文字母。
思路:哈希表+双指针
初始化子串左端点 l=0,枚举子串右端点 r。
对于示例 2,只要 r ≥1,那么合法子串是不能包含 le 的,所以左端点 l 必须向右移,不可能再回到 0(否则就包含 le 了)。因为左端点只会向右移动,不会向左移动,这样的单调性保证了算法的效率。
当r 右移到一个新的字母时,枚举以该字母为右端点的 forbidden[i] 的最短长度。如果发现子串 word[i] 到 word[right] 在 forbidden 中(用哈希表实现),那么更新 l =i+1 并结束枚举,从而避免合法子串包含 forbidden 中的字符串。枚举结束后,更新答案为合法子串长度 r −l +1 的最大值。
AC代码:
class Solution { public: int longestValidSubstring(string word, vector<string>& forbidden) { unordered_set<string> s(forbidden.begin(),forbidden.end()); int n=word.size(); int l=0,ans=0; for(int r=0;r<n;r++) { for(int i=r;i>=l&&i>r-10;i--) { if(s.count(word.substr(i,r-i+1))) { l=i+1; break; } } ans=max(ans,r-l+1); } return ans; } };