1 条题解
-
0
问题分析
我们需要找到一个最长的目标串 ,它可以被分割成若干 A 类串 ,并且对于任意相邻的两个 A 类串 和 ,存在一个被 对应的 A 类串支配的 B 类串,该 B 类串是 的前缀。
关键观察
-
图论建模:我们可以将这个问题建模为一个有向图:
- 节点:A 类串
- 边:从 到 当且仅当存在一个被 支配的 B 类串 ,且 是 的前缀
-
最长路径问题:在这个图中,我们要找的就是从任意节点开始的最长路径。如果图中存在环,则答案就是 (无限长)。
-
前缀关系检查:核心问题是如何高效地判断一个 B 类串是否是某个 A 类串的前缀。
解决方案
步骤 1:字符串处理基础
我们需要处理子串的前缀关系。对于字符串 ,我们可以:
- 构建后缀自动机 (Suffix Automaton) 或 后缀数组 (Suffix Array) 来高效处理子串关系
- 使用 字符串哈希 来快速比较子串
这里我们选择后缀自动机,因为它能很好地处理子串定位和前缀检查。
步骤 2:建立支配图
对于每个支配关系 :
- 支配
- 我们需要找到所有 A 类串 ,使得 是 的前缀
高效查找方法:
- 将 在后缀自动机中定位
- 找到所有包含 作为前缀的 A 类串
步骤 3:动态规划求最长路径
设 表示以第 个 A 类串结尾的最长目标串长度。
转移方程:
其中存在从 到 的边。
步骤 4:环检测
如果在动态规划过程中发现环(通过拓扑排序或 DFS 检测),则输出 。
算法实现细节
1. 后缀自动机构建
struct SuffixAutomaton { struct State { int len, link; map<char, int> next; }; vector<State> st; int last, sz; SuffixAutomaton(int maxlen) { st.resize(2 * maxlen); last = 0; sz = 1; st[0].len = 0; st[0].link = -1; } void extend(char c) { int cur = sz++; st[cur].len = st[last].len + 1; int p = last; while (p != -1 && !st[p].next.count(c)) { st[p].next[c] = cur; p = st[p].link; } if (p == -1) { st[cur].link = 0; } else { int q = st[p].next[c]; if (st[p].len + 1 == st[q].len) { st[cur].link = q; } else { int clone = sz++; st[clone] = st[q]; st[clone].len = st[p].len + 1; while (p != -1 && st[p].next[c] == q) { st[p].next[c] = clone; p = st[p].link; } st[q].link = st[cur].link = clone; } } last = cur; } };2. 子串定位
对于每个 A 类串和 B 类串,我们在后缀自动机中找到对应的状态节点。
3. 图构建算法
def build_graph(): graph = [[] for _ in range(n_a + 1)] # 预处理每个B类串对应的A类串列表 b_to_a = [[] for _ in range(n_b + 1)] for j in range(1, n_b + 1): B_j = get_B_string(j) # 获取B类串 # 在后缀自动机中查找所有以B_j为前缀的A类串 for i in range(1, n_a + 1): A_i = get_A_string(i) if is_prefix(B_j, A_i): b_to_a[j].append(i) # 根据支配关系构建图 for x, y in dominance_relations: for j in b_to_a[y]: graph[x].append(j) return graph4. 动态规划与环检测
def solve(): graph = build_graph() n = n_a # 计算入度 indegree = [0] * (n + 1) for i in range(1, n + 1): for j in graph[i]: indegree[j] += 1 # 拓扑排序 queue = deque() dp = [0] * (n + 1) for i in range(1, n + 1): if indegree[i] == 0: queue.append(i) dp[i] = len_A[i] # 初始值为A类串本身的长度 count = 0 while queue: u = queue.popleft() count += 1 for v in graph[u]: dp[v] = max(dp[v], dp[u] + len_A[v]) indegree[v] -= 1 if indegree[v] == 0: queue.append(v) if count < n: # 存在环 return -1 else: return max(dp)复杂度分析
- 后缀自动机构建:
- 子串定位:
- 图构建:(朴素方法),但可以通过优化达到
- 拓扑排序:
总复杂度:
优化策略
对于大数据范围,我们需要优化图构建过程:
- 使用字符串哈希:快速判断前缀关系
- 利用后缀数组:通过 LCP(最长公共前缀)数组快速找到所有以某个串为前缀的串
- 建立AC自动机:将所有的A类串建立AC自动机,然后用B类串去匹配
数学表达
问题形式化定义
设:
- 为 A 类串集合
- 为 B 类串集合
- $D \subseteq \{1, \dots, n_a\} \times \{1, \dots, n_b\}$ 为支配关系集合
定义有向图 ,其中:
- 当且仅当 使得 是 的前缀
目标:在 中找最长路径,若存在环则输出 。
动态规划方程
$$dp[i] = \max\begin{cases} |A_i| \\ \max\limits_{(j,i) \in E} (dp[j] + |A_i|) \end{cases} $$最终答案:
总结
本题的核心是将字符串的前缀关系转化为图论中的边,然后通过拓扑排序和动态规划求解最长路径。关键在于高效处理大量的字符串前缀检查,这需要借助后缀自动机、字符串哈希等高级字符串算法。
对于不同的数据范围,我们可以选择不同的优化策略,在时间复杂度和实现复杂度之间取得平衡。
-
- 1
信息
- ID
- 4522
- 时间
- 3000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 6
- 已通过
- 1
- 上传者