📅  最后修改于: 2023-12-03 15:28:47.680000             🧑  作者: Mango
本题为 "门门 CS 1997",是一个经典的算法题目,主要考察算法的优化能力,需要使用动态规划来解决问题。
给定一个长度为 n 的字符串 s 和 m 个模式串 p1,p2,...,pm,求在 s 中能否找到一个子串,使得该子串恰好匹配其中的任意一个模式串。
第一行包含两个整数 n,m,分别表示字符串 s 的长度和模式串的数量。
第二行包含一个长度为 n 的字符串 s。
接下来 m 行,每行一个字符串 pi,表示一个模式串。
如果存在匹配的子串,输出 "YES",否则输出 "NO"。
注:匹配必须是精确匹配,不能有任何多余或缺少的字符。
8 2
abcdeabc
abc
de
YES
我们可以枚举每一个模式串,并使用 KMP 算法进行匹配,时间复杂度是 O(nmlogm),不过可以通过优化 KMP 进一步优化时间复杂度。
时间复杂度:O(nmlogm)
空间复杂度:O(m)
代码片段:
def kmp(text, pattern):
n, m = len(text), len(pattern)
j = 0
nxt = get_next(pattern)
for i in range(n):
while j > 0 and text[i] != pattern[j]:
j = nxt[j-1]
if text[i] == pattern[j]:
j += 1
if j == m:
return True
return False
def get_next(pattern):
n = len(pattern)
nxt = [0] * n
j = 0
for i in range(1, n):
while j > 0 and pattern[i] != pattern[j]:
j = nxt[j-1]
if pattern[i] == pattern[j]:
j += 1
nxt[i] = j
return nxt
def solve(text, patterns):
for pattern in patterns:
if kmp(text, pattern):
return "YES"
return "NO"
n, m = map(int, input().split())
text = input()
patterns = [input().strip() for _ in range(m)]
print(solve(text, patterns))
动态规划,我们可以定义 dp[i][j] 表示以 i 结尾的子串是否匹配第 j 个模式串,状态转移方程为:
if s[i] == p[j]:
dp[i][j] = dp[i-1][j-1]
if s[i] != p[j]:
dp[i][j] = False
dp[i][j] |= dp[i-1][j]
时间复杂度:O(nm)
空间复杂度:O(nm)
代码片段:
def solve(text, patterns):
n, m = len(text), len(patterns)
dp = [[False] * (m+1) for _ in range(n+1)]
for i in range(n+1):
dp[i][0] = True
for i in range(1, n+1):
for j in range(1, m+1):
if text[i-1] == patterns[j-1]:
dp[i][j] = dp[i-1][j-1]
dp[i][j] |= dp[i-1][j]
return "YES" if any(dp[n]) else "NO"
n, m = map(int, input().split())
text = input()
patterns = [input().strip() for _ in range(m)]
print(solve(text, patterns))
AC 自动机,可以将模式串插入到 AC 自动机中,然后在文本串上进行匹配。时间复杂度为 O(n+m+∑|pi|),其中 ∑|pi| 表示所有模式串的总长度。
时间复杂度:O(n+m+∑|pi|)
空间复杂度:O(∑|pi|)
代码片段:
class TrieNode:
def __init__(self):
self.children = {}
self.fail = None
self.is_end = False
class AC:
def __init__(self):
self.root = TrieNode()
def insert(self, word):
node = self.root
for c in word:
if c not in node.children:
node.children[c] = TrieNode()
node = node.children[c]
node.is_end = True
def build_fail(self):
q = [self.root]
while q:
node = q.pop(0)
for ch, child in node.children.items():
if node == self.root:
child.fail = self.root
else:
fail_node = node.fail
while fail_node is not None:
if ch in fail_node.children:
child.fail = fail_node.children[ch]
break
fail_node = fail_node.fail
if fail_node is None:
child.fail = self.root
q.append(child)
def search(self, text):
node, res = self.root, []
for i, c in enumerate(text):
while node != self.root and c not in node.children:
node = node.fail
if c in node.children:
node = node.children[c]
temp = node
while temp != self.root:
if temp.is_end:
res.append((i, i-len(word)+1))
temp = temp.fail
return res
def solve(text, patterns):
ac = AC()
for pattern in patterns:
ac.insert(pattern)
ac.build_fail()
res = ac.search(text)
return "YES" if res else "NO"
n, m = map(int, input().split())
text = input().strip()
patterns = [input().strip() for _ in range(m)]
print(solve(text, patterns))