zoukankan      html  css  js  c++  java
  • 「POI 2005」SZA-Template 「失配树」「双向链表」「思维」

    先来观察答案的几个强性质。

    首先答案肯定是原串的一个( t{border}),也就是失配树上的一条链。

    再进一步观察:比如说答案在原串出现的位置分别为(p_1, p_2, p_3... p_k)(不妨设其严格升序),那么一定有(max (p_i - p_{i-1}) leq length(ans))

    你问我为什么?如果大于的话就接不上了啊...

    然后我们发现,只要满足上面那两个条件,那这个串一定就是个合法串。于是我们把这套东西搬到失配树上:

    1. 这个串是n在树上的祖先
    2. 这个串在树上的子树里所有节点的max_gap (leq) 这个串结尾的下标

    问题就变成了怎么维护max_gap.

    你发现这个东西维护最靠右边的1,最靠左边的1,答案就可以用线段树合并做到(mathcal{O(n log n)}),但是发现空间有点爆炸。

    于是发现,可选的串只在一条链上,所以可以先全加进去,然后再逐步删,就可以用线段树做到(mathcal{O(n log n)})时间,(mathcal{O(n)})空间了。

    然后发现这根本不用线段树,因为是逐步删答案只会不断变大,双向链表就可以解决。所以复杂度是(O(n))的。

    #include <bits/stdc++.h>
    
    #define test(...) fprintf(stderr, __VA_ARGS__)
    #define dbg(x) cerr << #x << " = " << x << '
    '
    
    using namespace std;
    
    typedef long long ll;
    typedef pair <int, int> pii;
    typedef vector <int> vi;
    typedef unsigned int ui;
    typedef vector <pair <int, int> > edges;
    
    const int N = 500010;
    int fail[N], n, pre[N], nxt[N], to[N]; 
    char s[N];
    vi g[N];
    int max_gap, ans = 0x7fffffff;
    void erase_point(int u) {
    	if (pre[u] && nxt[u]) max_gap = max(max_gap, nxt[u] - pre[u]);
    	pre[nxt[u]] = pre[u]; nxt[pre[u]] = nxt[u];
    }
    void del(int);
    void dfs(int u) {
    	if (max_gap <= u) ans = min(ans, u);
    	erase_point(u);
    	for (int i = 0; i < (int)g[u].size(); ++i) {
    		int v = g[u][i];
    		if (v != to[u]) del(v);
    	}
    	if (to[u]) dfs(to[u]);
    }
    void del(int u) {
    	erase_point(u);
    	for (int i = 0; i < (int)g[u].size(); ++i) {
    		int v = g[u][i];
    		del(v);
    	}
    }
    void solve() {
    	scanf ("%s", s + 1);
    	n = strlen(s + 1);
    	fail[1] = 0; 
    	for (int i = 2, j = 0; i <= n; ++i) {
    		while (j && s[j + 1] != s[i]) j = fail[j];
    		j += s[j + 1] == s[i];
    		fail[i] = j;
    	}
    	for (int i = 1; i <= n; ++i) pre[i] = i-1, nxt[i] = i+1;
    	nxt[0] = 1; pre[n + 1] = n;    
    	max_gap = 1; 
    	int p = n;
    	while (p) to[fail[p]] = p, p = fail[p];
    	for (int i = 1; i <= n; ++i) g[fail[i]].push_back(i);
    	dfs(0);
    	cout << ans << '
    '; 
    } 
    
    int main() {
    #ifdef LOCAL
    	freopen("sample.in", "r", stdin);
    #endif
      int tests = 1;
      while (tests--) solve();
      return 0;
    }
    
  • 相关阅读:
    storm源代码分析---Transactional spouts
    SQLServer 中存储过程
    实体添加映射
    SELECT INTO 和 INSERT INTO SELECT
    .NET System.Timers.Timer的原理和使用(开发定时执行程序)
    .net Framework 中的四种计时器
    AutoMapper 在你的项目里飞一会儿
    C#字符串、字节数组和内存流间的相互转换
    Entity Framework Code First使用者的福音 --- EF Power Tool使用记之二(问题探究)
    Entity Framework Code First使用者的福音 --- EF Power Tool使用记之一
  • 原文地址:https://www.cnblogs.com/LiM-817/p/12343176.html
Copyright © 2011-2022 走看看