• 【border相关】【P3426】 [POI2005]SZA-Template


    【border相关】【P3426】 [POI2005]SZA-Template

    Description

    给定一个字符串 (S),要求一个最短的字符串 (T),使得 (S) 可以由 (T) 不断在后面接上自身得到。在拼接的时候, (T) 的某个后缀如果与某个前缀相同,则相同的部分可以算作一个,不再重复出现。

    Limitations

    (1 leq |S| leq 5 imes 10^5)

    Solution

    介绍一个叫 (border) 树的东西,在 OI 中被称作 (next) 树。

    (S) 的前缀 (i) 的最长 (border)(border_i),考虑在 (i)(border_i) 之间连一条边,最终会形成一棵以 (0) 为根的树。

    证明上,考虑这棵树有 (n + 1) 个节点,而显然 (border_i < i),因此每个节点连向 (border) 的边都是互不重复的,共有 (n) 条边,由此可以证明这是一颗树。

    这棵树有两个优美的性质:

    节点 (u) 的祖先集合是 (u) 的所有 (border) 集合

    节点 (u) 的后代集合是 (u) 能作为 (border)(S) 的前缀子串集合

    对于性质 (1),根据定义,(u) 的父节点是 (u) 的最长 (border),迭代证明即可。

    性质 (2) 可以由性质 (1) 反推得到。

    现在考虑本题。

    一个显而易见的结论是 (T) 一定是 (S)(border)

    因此我们考虑枚举 (S) 的所有 (border),我们发现对于长度为 (i)(border),如果将他在 (border) 树上的后代拿下来排序以后相邻两数差值的最大值大于 (i),则这个 (border) 不能作为答案,因为对于插值最大的两个数,在拼接到左边的位置以后再加一个长度为 (i)(T) 不能拼接到右侧的数,反之可以证明这个 (border) 是合法的。

    我们考虑维护 (border) 的所有后代,从长到短枚举 (border) 时,相当于从 (border) 树的某个叶节点一直枚举到根,我们发现 (border) 变短时只会加入一些节点而不会删除,因此用一个 set 去维护这些后代,用 multiset 维护插值最大值即可。

    时间复杂度 (O(|S| log |S|))

    Code

    写代码的时候发现一个有关 multiset 的有趣的事:erase某个值的时候,会将全部的该值删掉,如果想要只删掉一个,需要 s.erase(s.find(x))

    #include <cstdio>
    #include <set>
    #include <vector>
    #include <algorithm>
    
    const int maxn = 500005;
    
    int n, ans;
    char MU[maxn];
    int border[maxn];
    std::set<int>s;
    std::multiset<int>ms;
    std::vector<int>son[maxn];
    
    void KMP();
    int ReadStr(char *p);
    void dfs(const int u);
    void update(const int x);
    
    void KMP();
    
    int main() {
      freopen("1.in", "r", stdin);
      n = ReadStr(MU + 1);
      KMP();
      update(n);
      for (int i = border[n], j = n; i; j = i, i = border[i]) {
        update(i);
        for (auto u : son[i]) if (u != j) {
          dfs(u);
        }
        if (*(--ms.end()) <= i) {
          ans = i;
        }
      }
      qw(ans, '
    ', true);
      return 0;
    }
    
    int ReadStr(char *p) {
      auto beg = p;
      do *p = IPT::GetChar(); while ((*p > 'z') || (*p < 'a'));
      do *(++p) = IPT::GetChar(); while ((*p >= 'a') && (*p <= 'z'));
      *p = 0;
      return p - beg;
    }
    
    void KMP() {
      for (int i = 2, j = 0; i <= n; ++i) {
        while (j && (MU[i] != MU[j + 1])) j = border[j];
        if (MU[i] == MU[j + 1]) ++j;
        son[border[i] = j].push_back(i);
      }
    }
    
    void dfs(const int u) {
      update(u);
      for (auto v : son[u]) {
        dfs(v);
      }
    }
    
    void update(const int x) {
      auto u = s.insert(x).first, ftmp = u, btmp = u;
      --ftmp; ++btmp;
      if ((u != s.begin()) && (btmp != s.end())) {
        ms.erase(ms.find(*btmp - *ftmp));
      }
      if (u != s.begin()) {
        ms.insert(x - *ftmp);
      }
      if (btmp != s.end()) {
        ms.insert(*btmp - x);
      }
    }
    
  • 相关阅读:
    [Swift]LeetCode32. 最长有效括号 | Longest Valid Parentheses
    [Swift]LeetCode31. 下一个排列 | Next Permutation
    [Swift]LeetCode30. 与所有单词相关联的字串 | Substring with Concatenation of All Words
    [Swift]LeetCode29. 两数相除 | Divide Two Integers
    时光轴的设计理念
    ITFriend开发日志20140611
    ITFriend开发日志20140611
    高中生活--第7篇–我为什么不交作业
    高中生活--第7篇–我为什么不交作业
    ITFriend网站内测公测感悟
  • 原文地址:https://www.cnblogs.com/yifusuyi/p/11470747.html
Copyright © 2020-2023  润新知