@[TOC]([python刷题模板] 子序列自动机 )
子序列自动机可以用来解决子序列判断问题:问模式串p是否是原串s的子序列。 当需要对同一个串进行多次不同模式串匹配时,可以预先对s进行自动机的构造。 用一次构造开销,节省询问开销。
这类问题朴素的做法显然是双指针:
我们发现:i后移时,一定会移动到后边第一个(最近的),与p[j]相同的字符上。那我们可以预处理出来原串上每个字符后边的所有字符最近出现的位置。 这就是子序列自动机的做法。
朴素做法, O(n+m)
自动机:
自动机构造复杂度 O(mc)*,c=26即为字典长度,m是原串长度。
每次匹配复杂度为 O(n)。
例题: 392. 判断子序列
直接询问。
class SubSequenceAuto: def __init__(self,s,abc='abcdefghijklmnopqrstuvwxyz'): self.s,self.abc = s,abc self.n,abc_len = len(s),len(abc) self.abc_index = {v:k for k,v in enumerate(abc)} self.dp = [[self.n]*abc_len for _ in range(self.n+1)] dp = self.dp # dp.append([self.n]*abc_len) for i in range(self.n-1,-1,-1): dp[i] = dp[i+1][:] dp[i][self.abc_index[s[i]]] = i # for j in range(abc_len): # dp[i][j] = i if s[i]==abc[j] else dp[i+1][j] def query_is_sub_seq(self,t): dp = self.dp abc_index = self.abc_index n = self.n r = 0 for c in t: r = dp[r][abc_index[c]] if r == n: return False r += 1 return True class Solution: def isSubsequence(self, s: str, t: str) -> bool: ssa = SubSequenceAuto(t) return ssa.query_is_sub_seq(s)
链接: 522. 最长特殊序列 II
这题正解应该是自动机,然而数据弱,每个单次长度<=10,所以可能不如朴素。
class SubSequenceAuto: def __init__(self,s,abc='abcdefghijklmnopqrstuvwxyz'): self.s,self.abc = s,abc self.n,abc_len = len(s),len(abc) self.abc_index = {v:k for k,v in enumerate(abc)} self.dp = [[self.n]*abc_len for _ in range(self.n+1)] dp = self.dp # dp.append([self.n]*abc_len) for i in range(self.n-1,-1,-1): dp[i] = dp[i+1][:] dp[i][self.abc_index[s[i]]] = i # for j in range(abc_len): # dp[i][j] = i if s[i]==abc[j] else dp[i+1][j] def query_is_sub_seq(self,t): dp = self.dp abc_index = self.abc_index n = self.n r = 0 for c in t: r = dp[r][abc_index[c]] if r == n: return False r += 1 return True class Solution: def findLUSlength(self, strs: List[str]) -> int: """ 先说一个显然:如果s的子序列ss是一个特殊序列,那么s更是特殊序列。 因此本题只需要判断每个字符串是否是其它字符串的子序列。 判断子序列可以双指针,或者用子序列自动机。 """ n = len(strs) flags = [True] * n # 每个字符串是否是特殊序列,初始化为0。如果他是别人的子序列,则置False # 以下判断j是不是i的子序列 for i in range(n): sba = SubSequenceAuto(strs[i]) for j in range(n): if i == j or flags[j] ==False: continue if sba.query_is_sub_seq(strs[j]): flags[j] = False ans = -1 for i in range(n): if flags[i]: ans = max(ans,len(strs[i])) return ans
人生苦短,我用Python!