回溯 + 贪心 + 滚动哈希
Problem: 3474. 字典序最小的生成字符串
[TOC]
思路
处理 T
如果str1[i] == T,那就可以确定res[i:i+m]上的字母,很划算,所以优先处理T,如果有冲突就直接return ""
n,m = len(str1),len(str2)
N = m+n-1
# 先处理T
res = [''] * N
for i in range(n):
if str1[i] == 'F':
continue
for j in range(m):
if res[i+j] == str2[j]:
continue
if res[i+j] == '':
res[i+j] = str2[j]
continue
# 有冲突
return ""
处理 F
滚动哈希
假设dfs(i-1)满足题意,然后往res[i]中填入新的字母w,那么:
如果str1[i-m+1] == F时,需要判断res[i-m+1:i+1]是否等于str2,如果不等于才能满足题意F。
为了快速判断res[i-m+1:i+1]是否等于str2,也就是经典字符串匹配,这里就直接用滚动哈希了:
-
pre数组,滚动哈希"前缀和" -
tgt:str2的字符串哈希值 -
pow_m = pow(base,m,mod):base的m次方 -
res:填入结果数组
# 回溯处理 F,并 贪心 获取结果
global ans
ans = ""
# 滚动哈希
pre = [0]
base, mod = 1331, 10**9 + 7
# base 的 m 次方
pow_m = pow(base,m,mod)
tgt = 0
for w in str2:
tgt = (tgt * base + ord(w)) % mod
回溯 + 贪心
在贪心填入新字母的过程中,需要同步更新与回溯:
-
pre哈希前缀和 -
res结果数组
# 到第i位
def dfs(i):
# 第一次构造成功后,赋值给 ans
global ans
if i == len(res):
ans = ''.join(res)
return True
# 当前值由于预处理`T`时已经填好了
if res[i] != '':
# 同步更新 pre 哈希前缀和
pre.append((pre[-1] * base + ord(res[i])) % mod)
# 判断 F
if i >= m - 1 and str1[i-m+1] == 'F' and (pre[-1] - pre[i+1-m] * pow_m) % mod == tgt:
# 回溯
pre.pop()
return False
if dfs(i+1):
return True
# 回溯
pre.pop()
return False
# 贪心 填入新字母
for w in ascii_lowercase:
# 同步更新 pre 哈希前缀和
pre.append((pre[-1] * base + ord(w)) % mod)
# 同步更新 res 结果集
res[i] = w
if i < m - 1:
if dfs(i+1):
return True
# 判断 F 符合题意
elif (pre[-1] - pre[i+1-m] * pow_m) % mod != tgt:
if dfs(i+1):
return True
# 回溯
pre.pop()
res[i] = ''
# 均不满足题意
return False
预校验 F
周赛没时间看这题,被T3卡常卡吐了,赛后看了下,也没多少思路,就试试暴力回溯能不能过吧,竟然过了,我也不知道时间复杂度是多少,快倒是挺快的:![]()
感觉不是正确做法,~有没有新样例能卡一下?~
找到个新增样例①卡了:
"FFFFFFFFFFFFFFFFFFFFFFFFTTFFT
"fff"
超时了,原因是str1后面加粗的部分是不满足题意的,因此在处理 T后,先预校验 F
# 预校验 F
for i in range(n):
if str1[i] == 'F':
for j in range(m):
# 只要存在一个字母不等即可
if res[i+j] != str2[j]:
break
else:
# 字符串相等了,不满足 F
return ""
如果这个预校验 F通过了,那回溯部分肯定能获取结果。
加了这个预处理后,没超时了,但感觉还是有问题,暂时找不到新样例卡回溯
更多题目模板总结,请参考2024年度总结与题目分享
Code
class Solution:
def generateString(self, str1: str, str2: str) -> str:
n,m = len(str1),len(str2)
N = m+n-1
# 先处理T
res = [''] * N
for i in range(n):
if str1[i] == 'F':
continue
for j in range(m):
if res[i+j] == str2[j]:
continue
if res[i+j] == '':
res[i+j] = str2[j]
continue
# 有冲突
return ""
# 回溯处理 F,并 贪心 获取结果
global ans
ans = ""
# 滚动哈希
pre = [0]
base, mod = 1331, 10**9 + 7
# base 的 m 次方
pow_m = pow(base,m,mod)
tgt = 0
for w in str2:
tgt = (tgt * base + ord(w)) % mod
# 到第i位
def dfs(i):
# 第一次构造成功后,赋值给 ans
global ans
if i == len(res):
ans = ''.join(res)
return True
# 当前值由于预处理`T`时已经填好了
if res[i] != '':
# 同步更新 pre 哈希前缀和
pre.append((pre[-1] * base + ord(res[i])) % mod)
# 判断 F
if i >= m - 1 and str1[i-m+1] == 'F' and (pre[-1] - pre[i+1-m] * pow_m) % mod == tgt:
# 回溯
pre.pop()
return False
if dfs(i+1):
return True
# 回溯
pre.pop()
return False
# 贪心 填入新字母
for w in ascii_lowercase:
# 同步更新 pre 哈希前缀和
pre.append((pre[-1] * base + ord(w)) % mod)
# 同步更新 res 结果集
res[i] = w
if i < m - 1:
if dfs(i+1):
return True
# 判断 F 符合题意
elif (pre[-1] - pre[i+1-m] * pow_m) % mod != tgt:
if dfs(i+1):
return True
# 回溯
pre.pop()
res[i] = ''
# 均不满足题意
return False
dfs(0)
return ans
class Solution:
def generateString(self, str1: str, str2: str) -> str:
n,m = len(str1),len(str2)
N = m+n-1
# 先处理T
res = [''] * N
for i in range(n):
if str1[i] == 'F':
continue
for j in range(m):
if res[i+j] == str2[j]:
continue
if res[i+j] == '':
res[i+j] = str2[j]
continue
# 有冲突
return ""
# 预校验 F
for i in range(n):
if str1[i] == 'F':
for j in range(m):
# 只要存在一个字母不等即可
if res[i+j] != str2[j]:
break
else:
# 字符串相等了,不满足 F
return ""
# 回溯处理 F,并 贪心 获取结果
# 滚动哈希
pre = [0]
base, mod = 1331, 10**9 + 7
# base 的 m 次方
pow_m = pow(base,m,mod)
tgt = 0
for w in str2:
tgt = (tgt * base + ord(w)) % mod
# 到第i位
def dfs(i):
if i == len(res):
return True
# 当前值由于预处理`T`时已经填好了
if res[i] != '':
# 同步更新 pre 哈希前缀和
pre.append((pre[-1] * base + ord(res[i])) % mod)
# 判断 F
if i >= m - 1 and str1[i-m+1] == 'F' and (pre[-1] - pre[i+1-m] * pow_m) % mod == tgt:
# 回溯
pre.pop()
return False
if dfs(i+1):
return True
# 回溯
pre.pop()
return False
# 贪心 填入新字母
for w in ['a','b']:
# 同步更新 pre 哈希前缀和
pre.append((pre[-1] * base + ord(w)) % mod)
# 同步更新 res 结果集
res[i] = w
if i < m - 1:
if dfs(i+1):
return True
# 判断 F 符合题意
elif (pre[-1] - pre[i+1-m] * pow_m) % mod != tgt:
if dfs(i+1):
return True
# 回溯
pre.pop()
res[i] = ''
# 均不满足题意
return False
dfs(0)
return "".join(res)