LC 3292. 形成目标字符串需要的最少字符串数 II
这题看了题解 https://leetcode.cn/problems/minimum-number-of-valid-strings-to-form-target-ii/solutions/2917929/ac-zi-dong-ji-pythonjavacgo-by-endlessch-hcqk/ 之前没有写过AC自动机,按照题解给的链接看了一下OI Wiki上的内容,大致搞清楚了AC自动机怎么写。这题用AC自动机还有一个前提就是尽可能匹配最长的前缀,如果有了这个特性之后就是一个DP算法了,最重要的就是知道当前位置可以匹配的最长前缀是多少。
class Solution: def minValidStrings(self, words: List[str], target: str) -> int: trie = ACTrie() for w in words: trie.add(w) trie.build_fail() n = len(target) dp = [0] * (n + 1) now = trie for i, c in enumerate(target): c = ord(c) - ord('a') now = now.child[c] if now is trie: return -1 dp[i + 1] = dp[i + 1 - now.length] + 1 return dp[-1]
ACTrie实现如下,实现有几个要点:
- root节点是fail的最根源,root节点其实是不匹配任何前缀的。
- 如果某个节点x的 `child[i]` 是空的话,可以将 `child[i]` 也设置成为fail节点,这样自动机就创建完成了。
- 整个 `build_fail` 是一个BFS过程,按照深度知道所有的fail节点。
class ACTrie: def __init__(self): self.child: List[ACTrie] = [None] * 26 self.fail = None self.length = 0 self.word = None def __repr__(self): return '%s' % (self.word if self.word else '?') def add(self, w): root = self for pos, c in enumerate(w): c = ord(c) - ord('a') if root.child[c] is None: x = ACTrie() root.child[c] = x root = root.child[c] root.length = pos + 1 # root.word = w[:pos + 1] def build_fail(self): root = self from collections import deque q = deque() for i, t in enumerate(root.child): if t is not None: t.fail = root q.append(t) else: root.child[i] = root while q: x = q.popleft() for i, t in enumerate(x.child): f = x.fail while f and f.child[i] is None: f = f.fail f = f.child[i] if f else root if t is not None: t.fail = f q.append(t) else: x.child[i] = f