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