目录
1. 问题描述
把字符串 s
看作是 “abcdefghijklmnopqrstuvwxyz”
的无限环绕字符串,所以 s
看起来是这样的:
"...zabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd...."
.
现在给定另一个字符串 p
。返回 s
中 唯一 的 p
的 非空子串 的数量 。
示例 1:
输入: p = "a" 输出: 1 解释: 字符串 s 中只有一个"a"子字符。
示例 2:
输入: p = "cac" 输出: 2 解释: 字符串 s 中的字符串“cac”只有两个子串“a”、“c”。.
示例 3:
输入: p = "zab" 输出: 6 解释: 在字符串 s 中有六个子串“z”、“a”、“b”、“za”、“ab”、“zab”。
提示:
1 <= p.length <= 10^5
p
由小写英文字母构成
2. 思路与算法
问题描述显得很生硬难懂,明显的翻译病。展开来理解的话如下所示:
输入一个字符串 p,输出 满足以下条件的p 的子串的数量,:
- 1,子串同时是 s 的子串
- 2,不重复计数(比如说,p='abcab',ab出现了两次,不重复计)
- 3,非空子串的定义是非空且连续的子序列
例1,输入 p="a",p 的子串有 "a",满足条件的非空子串的数量是 1
例2,输入 p="cac",p的非空子串有 "c", "a", "ca", "ac", "cac"
满足条件的子串只有"c", "a"
例3,输入 p="zab",p的子串有 "z", "a", "b", "za", "ab", "zab"
全部满足条件。
显而易见的是,如果p的一个子串满足条件,则该子串的子串也都满足条件。
对p从左向右扫描局部最长严格(循环)升序子串,比如说,p='abdefgabcd',其中局部最长严格(循环)升序子串分别是'ab', 'defg', 'abcd'等。只要针对每个局部最长子串分别计算其包含的满足条件的子串即可。对于每个局部最长严格(循环)升序子串如果其长度为L,则它所包含的满足题设条件的子串数显然为。
最长严格(循环)升序子串,是指一定是a后面跟b,b后面跟c,。。。,z后面跟a,的方式形成的子串。强调这个,是因为只有这种字符串才是s的子串。而s是循环的,所以z后面跟a是满足条件的。
对p从左向右扫描局部最长严格(循环)升序子串,只需要一次性遍历,当发现下一个字符与上一个字符不满足以上“严格(循环)升序”的要求时,则当前最长严格(循环)升序子串结束了,重新开始下一个最长严格(循环)升序子串的搜索。显而易见的是,最长严格(循环)升序子串之间是不重叠的。
但是以上处理没有排除子串的重复计数。比如说,如果p='abcdabc',"abc"会被统计到两次。进一步,比如说,p='abcdabceab',最后出现“ab”已经被前面的“abc”覆盖了,不应该再被统计了。
为了解决以上重复计数的问题,统计以每个字符作为结尾的最长连续子串的长度。例如:abcdbcd, 对于以d结尾的有abcd, bcd, cd和d, 而bcd、cd、d产生的子串都会被abcd所覆盖,这样就只需要针对“abcd”计数即可,它所包含的以“d”结尾的连续子串数量显然等于它的长度。而“abc”则另外作为以“c”结尾的连续子串,不作为“abcd”的子串来处理。
3. 代码实现
class Solution:
def findSubstringInWraproundString(self, p: str) -> int:
#if len(p)==1: return 1
ans = 0
d = dict.fromkeys(list(range(26)),0)
curMaxSubStrLen = 1
for k in range(len(p)):
if k==0:
curMaxSubStrLen = 1
elif (p[k]=='a' and p[k-1]=='z') or (ord(p[k])-ord(p[k-1]))==1:
curMaxSubStrLen += 1
else:
curMaxSubStrLen = 1
d[ord(p[k])-97] = max(d[ord(p[k])-97],curMaxSubStrLen)
#print(d)
for key,val in d.items():
ans += val
return ans
执行用时:100 ms, 在所有 Python3 提交中击败了36.49%的用户
内存消耗:15.3 MB, 在所有 Python3 提交中击败了7.72%的用户
回到总目录:Leetcode每日一题总目录(动态更新。。。)