1 条题解

  • 0
    @ 2025-10-28 17:28:56

    问题分析

    我们需要找到一个最长的目标串 TT,它可以被分割成若干 A 类串 t1,t2,,tkt_1, t_2, \dots, t_k,并且对于任意相邻的两个 A 类串 tit_iti+1t_{i+1},存在一个被 tit_i 对应的 A 类串支配的 B 类串,该 B 类串是 ti+1t_{i+1} 的前缀。

    关键观察

    1. 图论建模:我们可以将这个问题建模为一个有向图:

      • 节点:A 类串
      • 边:从 AiA_iAjA_j 当且仅当存在一个被 AiA_i 支配的 B 类串 BkB_k,且 BkB_kAjA_j 的前缀
    2. 最长路径问题:在这个图中,我们要找的就是从任意节点开始的最长路径。如果图中存在环,则答案就是 1-1(无限长)。

    3. 前缀关系检查:核心问题是如何高效地判断一个 B 类串是否是某个 A 类串的前缀。

    解决方案

    步骤 1:字符串处理基础

    我们需要处理子串的前缀关系。对于字符串 SS,我们可以:

    1. 构建后缀自动机 (Suffix Automaton)后缀数组 (Suffix Array) 来高效处理子串关系
    2. 使用 字符串哈希 来快速比较子串

    这里我们选择后缀自动机,因为它能很好地处理子串定位和前缀检查。

    步骤 2:建立支配图

    对于每个支配关系 (x,y)(x, y)

    • AxA_x 支配 ByB_y
    • 我们需要找到所有 A 类串 AjA_j,使得 ByB_yAjA_j 的前缀

    高效查找方法

    1. ByB_y 在后缀自动机中定位
    2. 找到所有包含 ByB_y 作为前缀的 A 类串

    步骤 3:动态规划求最长路径

    dp[i]dp[i] 表示以第 ii 个 A 类串结尾的最长目标串长度。

    转移方程:

    dp[j]=max(dp[j],dp[i]+Aj)dp[j] = \max(dp[j], dp[i] + |A_j|)

    其中存在从 AiA_iAjA_j 的边。

    步骤 4:环检测

    如果在动态规划过程中发现环(通过拓扑排序或 DFS 检测),则输出 1-1

    算法实现细节

    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 graph
    

    4. 动态规划与环检测

    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)
    

    复杂度分析

    • 后缀自动机构建O(S)O(|S|)
    • 子串定位O(S+na+nb)O(|S| + n_a + n_b)
    • 图构建O(na×nb)O(n_a \times n_b)(朴素方法),但可以通过优化达到 O(m+na+nb)O(m + n_a + n_b)
    • 拓扑排序O(na+m)O(n_a + m)

    总复杂度:O(S+na+nb+m)O(|S| + n_a + n_b + m)

    优化策略

    对于大数据范围,我们需要优化图构建过程:

    1. 使用字符串哈希:快速判断前缀关系
    2. 利用后缀数组:通过 LCP(最长公共前缀)数组快速找到所有以某个串为前缀的串
    3. 建立AC自动机:将所有的A类串建立AC自动机,然后用B类串去匹配

    数学表达

    问题形式化定义

    设:

    • A={A1,A2,,Ana}A = \{A_1, A_2, \dots, A_{n_a}\} 为 A 类串集合
    • B={B1,B2,,Bnb}B = \{B_1, B_2, \dots, B_{n_b}\} 为 B 类串集合
    • $D \subseteq \{1, \dots, n_a\} \times \{1, \dots, n_b\}$ 为支配关系集合

    定义有向图 G=(V,E)G = (V, E),其中:

    • V={1,2,,na}V = \{1, 2, \dots, n_a\}
    • (i,j)E(i, j) \in E 当且仅当 (i,k)D\exists (i, k) \in D 使得 BkB_kAjA_j 的前缀

    目标:在 GG 中找最长路径,若存在环则输出 1-1

    动态规划方程

    $$dp[i] = \max\begin{cases} |A_i| \\ \max\limits_{(j,i) \in E} (dp[j] + |A_i|) \end{cases} $$

    最终答案:max1inadp[i]\max\limits_{1 \leq i \leq n_a} dp[i]

    总结

    本题的核心是将字符串的前缀关系转化为图论中的边,然后通过拓扑排序和动态规划求解最长路径。关键在于高效处理大量的字符串前缀检查,这需要借助后缀自动机、字符串哈希等高级字符串算法。

    对于不同的数据范围,我们可以选择不同的优化策略,在时间复杂度和实现复杂度之间取得平衡。

    • 1

    信息

    ID
    4522
    时间
    3000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    6
    已通过
    1
    上传者