原题链接: 467. 环绕字符串中唯一的子字符串
题目大意
把字符串 s
看作是 “abcdefghijklmnopqrstuvwxyz” 的无限环绕字符串,所以 s
看起来是这样的:
“…zabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd…” .
现在给定另一个字符串 p
。返回 s
中 唯一 的 p
的 非空子串 的数量 。
示例:
输入: p = "a"
输出: 1
解释: 字符串 s 中只有一个"a"子字符。
输入: p = "cac"
输出: 2
解释: 字符串 s 中的字符串“cac”只有两个子串“a”、“c”。
输入: p = "zab"
输出: 6
解释: 在字符串 s 中有六个子串“z”、“a”、“b”、“za”、“ab”、“zab”。
数据范围:
1 <= p.length <= 105
p 由小写英文字母构成
思路
- 双指针算法,将字符串划分为几段,每段内字符“连续”,不同段之间字符不“连续”
例如"cac"可分为"c",“a”, "c"共三段,"zab"可分为"zab"共一段。 - 题目要求唯一的非空连续字符串。考虑唯一性,一个字符串可由其
开头字符
以及字符串长度
唯一确定。本题只需求出每类字母(最多26类)开头的字符串的最大长度,用哈希表或数组存储与更新,最终累加即可得到答案。(若存在以’a’开头的长度为n的连续字符串,则由’a’开头的满足条件的连续字符串共有n个)。
例如"c",“a”, "c"可得到cnt[‘a’]=1,cnt[‘c’]=1,累加得答案为2;
例如"zab"可得到:
cnt[‘z’]=3 (zab, za, z)
cnt[‘a’]=2 (ab, a)
cnt[‘b’]=1 (b)
累加得答案为6. - 若题目不要求唯一性,则直接对1中的每个连续分段累加,设每个连续分段的长度为k,则该分段可贡献1+2+…+k=k(k+1)/2个子字符串数量。
例如"cac"可分为"c",“a”, "c"共三段:
‘c’:12/2=1
‘a’:12/2=1
‘b’:1*2/2=1 共3个
代码
class Solution {
public:
int findSubstringInWraproundString(string p) {
unordered_map<char, int> cnt;
for(int i = 0; i < p.size(); ){
// 分段,j用来指向下一段的开头字符
int j = i + 1;
while(j < p.size() && p[j] - p[j - 1] == 1 || p[j] == 'a' && p[j - 1] == 'z') j ++; // 连续字符串
// 更新各类字母开头的连续字符串的最大长度
while(i < j) cnt[p[i]] = max(cnt[p[i]], j - i), i ++ ;
}
int res = 0;
for(auto [k, v] : cnt){
res += v;
}
return res;
}
};
复杂度
时间复杂度:双指针需要
O
(
n
)
O(n)
O(n)
空间复杂度:哈希表存26个字母开头的最大长度,仅需要常数的额外空间