目录
1180. 统计只含单一字母的子串
https://leetcode-cn.com/problems/count-substrings-with-only-one-distinct-letter/
给你一个字符串 S,返回只含 单一字母 的子串个数。
示例 1:输入: "aaaba",输出: 8
解释: 只含单一字母的子串分别是 "aaa", "aa", "a", "b"。
"aaa" 出现 1 次。"aa" 出现 2 次。"a" 出现 4 次。"b" 出现 1 次。所以答案是 1 + 2 + 4 + 1 = 8。
示例 2:输入: "aaaaaaaaaa",输出: 55
提示:1 <= S.length <= 1000,S[i] 仅由小写英文字母组成。
题解
一:pre用来记录前一个字符,若相同,将该字符加入s,并用来计算加入一个字符带来的res的增加,我们可以知道,一个字符个数加1(两个字符数加1...整个s长度的字符个数加1),故我们可以知道增加的个数是加入该字符后,字符串的长度。若不相同,加的个数是1.其实我们并不关心s是啥,只关心其长度,可以优化一下空间,以及时间效率(字符串赋值带来的开销).
class Solution(object):
def countLetters(self, S):
"""
:type S: str
:rtype: int
"""
s, pre, res = "", None, 0
for c in S:
if c == pre:
s += c
res += len(s)
else:
s = c
res += 1
pre = c
return res
class Solution(object):
def countLetters(self, S):
local_len, pre, res = 0, None, 0
for c in S:
if c == pre:
local_len += 1
res += local_len
else:
local_len = 1
res += local_len
pre = c
return res
893. 特殊等价字符串组
https://leetcode-cn.com/problems/groups-of-special-equivalent-strings/
你将得到一个字符串数组 A。每次移动都可以交换 S 的任意两个偶数下标的字符或任意两个奇数下标的字符。如果经过任意次数的移动,S == T,那么两个字符串 S 和 T 是 特殊等价 的。
例如,S = "zzxy" 和 T = "xyzz" 是一对特殊等价字符串,因为可以先交换 S[0] 和 S[2],然后交换 S[1] 和 S[3],使得 "zzxy" -> "xzzy" -> "xyzz" 。
现在规定,A 的 一组特殊等价字符串 就是 A 的一个同时满足下述条件的非空子集:
- 该组中的每一对字符串都是 特殊等价 的
- 该组字符串已经涵盖了该类别中的所有特殊等价字符串,容量达到理论上的最大值(也就是说,如果一个字符串不在该组中,那么这个字符串就 不会 与该组内任何字符串特殊等价)
返回 A 中特殊等价字符串组的数量。
提示:1 <= A.length <= 1000,1 <= A[i].length <= 20,所有 A[i] 都具有相同的长度。所有 A[i] 都只由小写字母组成。
题解
一:仔细思考一下,其实只要偶数位上的字符的组合一样,奇数位上的字符的组合也一样的话,就是特殊等价,因为只要偶数位一样,那么我们通过偶数位的两两交换必能相等,奇数位同理。我们用rec[i]数组来记录偶数位和奇数位的字符的个数,0-25是偶数位,26-51是奇数位。然后比较的时候,只要这52个元素对应相等就ok了,已经和一个字符串(B)特殊等价了,所有能特殊等价的必定也和(B)等价,也不会和不能和B等价的等价,所以只要等价了,后面就可以不用就比较了。
class Solution(object):
def numSpecialEquivGroups(self, A):
"""
:type A: List[str]
:rtype: int
"""
def is_equiv(x, y):
for j in range(52):
if rec[x][j] != rec[y][j]:
return False
return True
res, n = 0, len(A)
equiv = [False] * n
rec = [[0] * 52 for _ in range(n)]
for i in range(n):
for j in range(len(A[i])):
idx = (j % 2) * 26 + ord(A[i][j]) - ord('a')
rec[i][idx] += 1
for i in range(n):
if equiv[i]:
continue
for j in range(i + 1, n):
if equiv[j]:
continue
if is_equiv(i, j):
equiv[j] = True
res += 1
return res
二:在法一的基础上,我们将rec数组映射成字符串,用集合来帮助去重。
class Solution(object):
def numSpecialEquivGroups(self, A):
n = len(A)
rec = [[0] * 52 for _ in range(n)]
for i in range(n):
for j in range(len(A[i])):
idx = ord(A[i][j]) - ord('a')
rec[i][(j % 2) * 26 + idx] += 1
return len({''.join(str(c) for c in rec[i]) for i in range(n)})
459. 重复的子字符串
https://leetcode-cn.com/problems/repeated-substring-pattern/
给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。
示例 1:输入: "abab",输出: True。解释: 可由子字符串 "ab" 重复两次构成。
示例 2:输入: "aba",输出: False
示例 3:输入: "abcabcabcabc",输出: True。解释: 可由子字符串 "abc" 重复四次构成。 (或者子字符串 "abcabc" 重复两次构成。)
题解
一:
class Solution(object):
def repeatedSubstringPattern(self, s):
"""
:type s: str
:rtype: bool
"""
if not s:
return True
for i in range(len(s)):
# 整个字符串的长度必定是s[:i + 1]的倍数,否则s[:i + 1]不可能是重复单元
if len(s) % (i + 1) != 0:
continue
if i == len(s) - 1:
return False
length = i + 1
flag = True
for j in range(i + 1, len(s), length):
end = min(len(s), j + length)
if s[j: end] != s[: i + 1]:
flag = False
break
if flag:
return True
return False
如果您的字符串S包含一个重复的子字符串,那么这意味着您可以多次“移位和换行”您的字符串,并使其与原始字符串匹配。
例如:abcabc
移位一次:cabcab
移位两次:bcabca
移位三次:abcabc现在字符串和原字符串匹配了,所以可以得出结论存在重复的子串
基于这个思想,可以每次移动k个字符,直到匹配移动length - 1次。但是这样对于重复字符串很长的字符串,效率会非常低。在LeetCode中执行时间超时了。为了避免这种无用的环绕,可以创建一个新的字符串str,它等于原来的字符串S再加上S自身,这样其实就包含了所有移动的字符串。
比如字符串:S = acd,那么str = S + S = acdacd
acd移动的可能:dac、cda。其实都包含在了str中了。就像一个滑动窗口
一开始acd (acd) ,移动一次ac(dac)d,移动两次a(cda)cd。循环结束
所以可以直接判断str中去除首尾元素之后,是否包含自身元素。如果包含。则表明存在重复子串。
class Solution(object):
def repeatedSubstringPattern(self, s):
return s in (s + s)[1: -1]