算法总览
题目
1371.每个元音包含偶数次的最长子字符串
思路分析:
就是得使用前缀和记录情况,dp[i][j]表示s[0] 到s[i] 中,j出现的次数
前缀和+剪枝
class Solution:
def findTheLongestSubstring(self, s: str) -> int:
# 使用字典将元音映射为数字,方便后续的记录
i_mapper = {
"a": 0,
"e": 1,
"i": 2,
"o": 3,
"u": 4
}
n = len(s)
# pre[i][j]表示s[0] 到 s[i] 之间字符j所出现的次数
pre = [[0] * 5 for _ in range(n)]
# pre
for i in range(n):
for j in range(5):
# 注意这里其实没有对i=0进行处理,因为-1在python中表示最后一个元素,所以不会越界报错
if s[i] in i_mapper and i_mapper[s[i]] == j:
pre[i][j] = pre[i - 1][j] + 1
else:
pre[i][j] = pre[i - 1][j]
# check(l,r)表示查询s[l]到s[r]中的情况
def check(l, r):
for i in range(5):
# 特别处理s[l]的情况,不然就是 pre[r][i] - pre[l-1][i],这个时候就得判断l==0的情况
if s[l] in i_mapper and i == i_mapper[s[l]]: cnt = 1
else: cnt = 0
if (pre[r][i] - pre[l][i] + cnt) % 2 != 0: return False
return True
# 由于是剪枝,i从最长的子序列的长度对应的末尾的下标开始计算
for i in range(n - 1, -1, -1):
# j表示长度为i的子序列的开始的下标
for j in range(n - i):
if check(j, i + j):
return i + 1
return 0
前缀和+状态压缩
class Solution:
def findTheLongestSubstring(self, s: str) -> int:
mapper = {
"a": 1,
"e": 2,
"i": 4,
"o": 8,
"u": 16
}
# seen使用哈希表存储每一个状态组合所第一次出现的下标,最多就是2^5就是32种情况
seen = {0: -1}
# res 用于记录更新答案,cur用于计算当前的奇偶组合的值
res = cur = 0
for i in range(len(s)):
if s[i] in mapper:
cur ^= mapper.get(s[i])
# 全部奇偶性都相同,相减一定都是偶数
if cur in seen:
res = max(res, i - seen.get(cur))
else:
seen[cur] = i
return res
class Solution:
def maxDifference(self, s: str, k: int) -> int:
s = list(map(int, s))
ans = -inf
for x in range(5):
for y in range(5):
if y == x:
continue
#cur_s 记录当前0,1,2,3,4出现的次数,pre_s则是先前的情况
# cur_s是维护0-i的情况,pre_s是维护0-left的情况
cur_s = [0] * 5
pre_s = [0] * 5
#
min_s = [[inf, inf], [inf, inf]]
left = 0
for i, v in enumerate(s):
cur_s[v] += 1
r = i + 1
# 一直在维护左边界的情况,r-left>=k
while r - left >= k and cur_s[x] > pre_s[x] and cur_s[y] > pre_s[y]:
# 检验是奇数还是偶数,奇数&1为1,偶数&1为0
p, q = pre_s[x] & 1, pre_s[y] & 1
min_s[p][q] = min(min_s[p][q], pre_s[x] - pre_s[y])
pre_s[s[left]] += 1
left += 1
if r >= k:
# cur_s[x] & 1 ^ 1 和 cur_s[y] & 1 奇偶不同
ans = max(ans, cur_s[x] - cur_s[y] - min_s[cur_s[x] & 1 ^ 1][cur_s[y] & 1])
return ans